blob: fb1446572ee36cf362c2112cb5140a01368b3aa4 [file] [log] [blame]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001#!/usr/bin/env python
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
4import gc
Paul Vinciguerra72f00042018-11-25 11:05:13 -08005import sys
Ole Trøan162989e2018-11-26 10:27:50 +00006import os
7import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04008import signal
Ole Trøan162989e2018-11-26 10:27:50 +00009import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +020010import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020011import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080012import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000013import random
14import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080015import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010016import platform
Ole Trøan162989e2018-11-26 10:27:50 +000017from collections import deque
18from threading import Thread, Event
19from inspect import getdoc, isclass
20from traceback import format_exception
21from logging import FileHandler, DEBUG, Formatter
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070022
23import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000024from scapy.packet import Raw
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040025import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080026from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010027from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000028from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070029from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000030from vpp_papi_provider import VppPapiProvider
31from vpp_papi.vpp_stats import VPPStats
Paul Vinciguerra499ea642019-03-15 09:39:19 -070032from vpp_papi.vpp_transport_shmem import VppTransportShmemIOError
Ole Trøan162989e2018-11-26 10:27:50 +000033from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
34 get_logger, colorize
35from vpp_object import VppObjectRegistry
36from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020037from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
38from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
39from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080040
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010041if os.name == 'posix' and sys.version_info[0] < 3:
42 # using subprocess32 is recommended by python official documentation
43 # @ https://docs.python.org/2/library/subprocess.html
44 import subprocess32 as subprocess
45else:
46 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020047
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080048# Python2/3 compatible
49try:
50 input = raw_input
51except NameError:
52 pass
53
juraj.linkescae64f82018-09-19 15:01:47 +020054PASS = 0
55FAIL = 1
56ERROR = 2
57SKIP = 3
58TEST_RUN = 4
59
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040060
61class BoolEnvironmentVariable(object):
62
63 def __init__(self, env_var_name, default='n', true_values=None):
64 self.name = env_var_name
65 self.default = default
66 self.true_values = true_values if true_values is not None else \
67 ("y", "yes", "1")
68
69 def __bool__(self):
70 return os.getenv(self.name, self.default).lower() in self.true_values
71
72 if sys.version_info[0] == 2:
73 __nonzero__ = __bool__
74
75 def __repr__(self):
76 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
77 (self.name, self.default, self.true_values)
78
79
80debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
81if debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010082 import debug_internal
83
Klement Sekeraf62ae122016-10-11 11:47:09 +020084"""
85 Test framework module.
86
87 The module provides a set of tools for constructing and running tests and
88 representing the results.
89"""
90
Klement Sekeraf62ae122016-10-11 11:47:09 +020091
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040092class VppDiedError(Exception):
93 """ exception for reporting that the subprocess has died."""
94
95 signals_by_value = {v: k for k, v in signal.__dict__.items() if
96 k.startswith('SIG') and not k.startswith('SIG_')}
97
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040098 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040099 self.rv = rv
100 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400101 self.testcase = testcase
102 self.method_name = method_name
103
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400104 try:
105 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400106 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400107 pass
108
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400109 if testcase is None and method_name is None:
110 in_msg = ''
111 else:
112 in_msg = 'running %s.%s ' % (testcase, method_name)
113
114 msg = "VPP subprocess died %sunexpectedly with return code: %d%s." % (
115 in_msg,
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400116 self.rv,
Paul Vinciguerraf7457522019-07-13 09:35:38 -0400117 ' [%s]' % (self.signal_name if
118 self.signal_name is not None else ''))
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400119 super(VppDiedError, self).__init__(msg)
120
121
Damjan Marionf56b77a2016-10-03 19:44:57 +0200122class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200123 """Private class to create packet info object.
124
125 Help process information about the next packet.
126 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200127 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100128 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200129 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100130 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200131 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100132 #: Store the index of the destination packet generator interface
133 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200134 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100135 #: Store expected ip version
136 ip = -1
137 #: Store expected upper protocol
138 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100139 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200140 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200141
Matej Klotton16a14cd2016-12-07 15:09:13 +0100142 def __eq__(self, other):
143 index = self.index == other.index
144 src = self.src == other.src
145 dst = self.dst == other.dst
146 data = self.data == other.data
147 return index and src and dst and data
148
Klement Sekeraf62ae122016-10-11 11:47:09 +0200149
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100150def pump_output(testclass):
151 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100152 stdout_fragment = ""
153 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400154 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100155 readable = select.select([testclass.vpp.stdout.fileno(),
156 testclass.vpp.stderr.fileno(),
157 testclass.pump_thread_wakeup_pipe[0]],
158 [], [])[0]
159 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100160 read = os.read(testclass.vpp.stdout.fileno(), 102400)
161 if len(read) > 0:
162 split = read.splitlines(True)
163 if len(stdout_fragment) > 0:
164 split[0] = "%s%s" % (stdout_fragment, split[0])
165 if len(split) > 0 and split[-1].endswith("\n"):
166 limit = None
167 else:
168 limit = -1
169 stdout_fragment = split[-1]
170 testclass.vpp_stdout_deque.extend(split[:limit])
171 if not testclass.cache_vpp_output:
172 for line in split[:limit]:
173 testclass.logger.debug(
174 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100175 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100176 read = os.read(testclass.vpp.stderr.fileno(), 102400)
177 if len(read) > 0:
178 split = read.splitlines(True)
179 if len(stderr_fragment) > 0:
180 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan7f991832018-12-06 17:35:12 +0100181 if len(split) > 0 and split[-1].endswith(b"\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100182 limit = None
183 else:
184 limit = -1
185 stderr_fragment = split[-1]
186 testclass.vpp_stderr_deque.extend(split[:limit])
187 if not testclass.cache_vpp_output:
188 for line in split[:limit]:
189 testclass.logger.debug(
190 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800191 # ignoring the dummy pipe here intentionally - the
192 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200193
194
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800195def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400196 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100197
Klement Sekera6aa58b72019-05-16 14:34:55 +0200198
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800199is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100200
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800201
202def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100203 return platform.machine() == 'aarch64'
204
Klement Sekera6aa58b72019-05-16 14:34:55 +0200205
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800206is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100207
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800208
209def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400210 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100211
Klement Sekera6aa58b72019-05-16 14:34:55 +0200212
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800213running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100214
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800215
216def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100217 os_id = os.getenv("OS_ID", "")
218 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200219
Klement Sekera6aa58b72019-05-16 14:34:55 +0200220
Klement Sekera3a350702019-09-02 14:26:26 +0000221running_on_centos = _running_on_centos()
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800222
Klement Sekerad3e671e2017-09-29 12:36:37 +0200223
Klement Sekera909a6a12017-08-08 04:33:53 +0200224class KeepAliveReporter(object):
225 """
226 Singleton object which reports test start to parent process
227 """
228 _shared_state = {}
229
230 def __init__(self):
231 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800232 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200233
234 @property
235 def pipe(self):
236 return self._pipe
237
238 @pipe.setter
239 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800240 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200241 raise Exception("Internal error - pipe should only be set once.")
242 self._pipe = pipe
243
juraj.linkes40dd73b2018-09-21 13:55:16 +0200244 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200245 """
246 Write current test tmpdir & desc to keep-alive pipe to signal liveness
247 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200248 if self.pipe is None:
249 # if not running forked..
250 return
251
Klement Sekera909a6a12017-08-08 04:33:53 +0200252 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200253 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200254 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200255 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200256
Dave Wallacee2efd122017-09-30 22:04:21 -0400257 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200258
259
Damjan Marionf56b77a2016-10-03 19:44:57 +0200260class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100261 """This subclass is a base class for VPP test cases that are implemented as
262 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200263 """
264
Ole Troana45dc072018-12-21 16:04:22 +0100265 extra_vpp_punt_config = []
266 extra_vpp_plugin_config = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100267
Klement Sekeraf62ae122016-10-11 11:47:09 +0200268 @property
269 def packet_infos(self):
270 """List of packet infos"""
271 return self._packet_infos
272
Klement Sekeradab231a2016-12-21 08:50:14 +0100273 @classmethod
274 def get_packet_count_for_if_idx(cls, dst_if_index):
275 """Get the number of packet info for specified destination if index"""
276 if dst_if_index in cls._packet_count_for_dst_if_idx:
277 return cls._packet_count_for_dst_if_idx[dst_if_index]
278 else:
279 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200280
281 @classmethod
282 def instance(cls):
283 """Return the instance of this testcase"""
284 return cls.test_instance
285
Damjan Marionf56b77a2016-10-03 19:44:57 +0200286 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200287 def set_debug_flags(cls, d):
288 cls.debug_core = False
289 cls.debug_gdb = False
290 cls.debug_gdbserver = False
291 if d is None:
292 return
293 dl = d.lower()
294 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200295 cls.debug_core = True
296 elif dl == "gdb":
297 cls.debug_gdb = True
298 elif dl == "gdbserver":
299 cls.debug_gdbserver = True
300 else:
301 raise Exception("Unrecognized DEBUG option: '%s'" % d)
302
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800303 @staticmethod
304 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200305 cpu_usage_list = [set(range(psutil.cpu_count()))]
306 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
307 if 'vpp_main' == p.info['name']]
308 for vpp_process in vpp_processes:
309 for cpu_usage_set in cpu_usage_list:
310 try:
311 cpu_num = vpp_process.cpu_num()
312 if cpu_num in cpu_usage_set:
313 cpu_usage_set_index = cpu_usage_list.index(
314 cpu_usage_set)
315 if cpu_usage_set_index == len(cpu_usage_list) - 1:
316 cpu_usage_list.append({cpu_num})
317 else:
318 cpu_usage_list[cpu_usage_set_index + 1].add(
319 cpu_num)
320 cpu_usage_set.remove(cpu_num)
321 break
322 except psutil.NoSuchProcess:
323 pass
324
325 for cpu_usage_set in cpu_usage_list:
326 if len(cpu_usage_set) > 0:
327 min_usage_set = cpu_usage_set
328 break
329
330 return random.choice(tuple(min_usage_set))
331
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800332 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200333 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200334 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400335 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100336 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400337 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100338 c = os.getenv("CACHE_OUTPUT", "1")
339 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200340 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100341 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
342 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400343 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100344 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
345 plugin_path = None
346 if cls.plugin_path is not None:
347 if cls.extern_plugin_path is not None:
348 plugin_path = "%s:%s" % (
349 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100350 else:
351 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100352 elif cls.extern_plugin_path is not None:
353 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100354 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100355 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100356 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100357 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100358 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100359 if size is not None:
360 coredump_size = "coredump-size %s" % size
361 if coredump_size is None:
362 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200363
Ole Troana45dc072018-12-21 16:04:22 +0100364 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000365 if not hasattr(cls, "worker_config"):
366 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200367
Ole Troana45dc072018-12-21 16:04:22 +0100368 cls.vpp_cmdline = [cls.vpp_bin, "unix",
369 "{", "nodaemon", debug_cli, "full-coredump",
370 coredump_size, "runtime-dir", cls.tempdir, "}",
371 "api-trace", "{", "on", "}", "api-segment", "{",
372 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000373 "main-core", str(cpu_core_number),
374 cls.worker_config, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200375 "statseg", "{", "socket-name", cls.stats_sock, "}",
376 "socksvr", "{", "socket-name", cls.api_sock, "}",
377 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100378 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200379 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100380 "}", "plugin", "unittest_plugin.so", "{", "enable",
381 "}"] + cls.extra_vpp_plugin_config + ["}", ]
382 if cls.extra_vpp_punt_config is not None:
383 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100384 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100385 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400386 if cls.test_plugin_path is not None:
387 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
388
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100389 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
390 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200391
392 @classmethod
393 def wait_for_enter(cls):
394 if cls.debug_gdbserver:
395 print(double_line_delim)
396 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
397 elif cls.debug_gdb:
398 print(double_line_delim)
399 print("Spawned VPP with PID: %d" % cls.vpp.pid)
400 else:
401 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
402 return
403 print(single_line_delim)
404 print("You can debug the VPP using e.g.:")
405 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400406 print("sudo gdb " + cls.vpp_bin +
407 " -ex 'target remote localhost:7777'")
Klement Sekera277b89c2016-10-28 13:20:27 +0200408 print("Now is the time to attach a gdb by running the above "
409 "command, set up breakpoints etc. and then resume VPP from "
410 "within gdb by issuing the 'continue' command")
411 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400412 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Klement Sekera277b89c2016-10-28 13:20:27 +0200413 print("Now is the time to attach a gdb by running the above "
414 "command and set up breakpoints etc.")
415 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800416 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200417
418 @classmethod
419 def run_vpp(cls):
420 cmdline = cls.vpp_cmdline
421
422 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100423 gdbserver = '/usr/bin/gdbserver'
424 if not os.path.isfile(gdbserver) or \
425 not os.access(gdbserver, os.X_OK):
426 raise Exception("gdbserver binary '%s' does not exist or is "
427 "not executable" % gdbserver)
428
429 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200430 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
431
Klement Sekera931be3a2016-11-03 05:36:01 +0100432 try:
433 cls.vpp = subprocess.Popen(cmdline,
434 stdout=subprocess.PIPE,
435 stderr=subprocess.PIPE,
436 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800437 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800438 cls.logger.critical("Subprocess returned with non-0 return code: ("
439 "%s)", e.returncode)
440 raise
441 except OSError as e:
442 cls.logger.critical("Subprocess returned with OS error: "
443 "(%s) %s", e.errno, e.strerror)
444 raise
445 except Exception as e:
446 cls.logger.exception("Subprocess returned unexpected from "
447 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100448 raise
449
Klement Sekera277b89c2016-10-28 13:20:27 +0200450 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100451
Damjan Marionf56b77a2016-10-03 19:44:57 +0200452 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200453 def wait_for_stats_socket(cls):
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000454 deadline = time.time() + 300
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800455 ok = False
456 while time.time() < deadline or \
457 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200458 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800459 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200460 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700461 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800462 if not ok:
463 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200464
465 @classmethod
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400466 def wait_for_coredump(cls):
467 corefile = cls.tempdir + "/core"
468 if os.path.isfile(corefile):
469 cls.logger.error("Waiting for coredump to complete: %s", corefile)
470 curr_size = os.path.getsize(corefile)
471 deadline = time.time() + 60
472 ok = False
473 while time.time() < deadline:
474 cls.sleep(1)
475 size = curr_size
476 curr_size = os.path.getsize(corefile)
477 if size == curr_size:
478 ok = True
479 break
480 if not ok:
481 cls.logger.error("Timed out waiting for coredump to complete:"
482 " %s", corefile)
483 else:
484 cls.logger.error("Coredump complete: %s, size %d",
485 corefile, curr_size)
486
487 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200488 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200489 """
490 Perform class setup before running the testcase
491 Remove shared memory files, start vpp and connect the vpp-api
492 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800493 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100494 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100495 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100496 cls.logger = get_logger(cls.__name__)
497 if hasattr(cls, 'parallel_handler'):
498 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100499 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700500
Klement Sekeraf62ae122016-10-11 11:47:09 +0200501 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200502 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200503 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200504 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200505 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
506 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100507 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
508 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200509 cls.file_handler.setLevel(DEBUG)
510 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700511 cls.logger.debug("--- setUpClass() for %s called ---" %
512 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200513 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200514 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200515 cls.logger.info("Temporary dir is %s, shm prefix is %s",
516 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200517 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100518 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100519 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200520 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100521 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100522 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200523 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200524 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200525 # need to catch exceptions here because if we raise, then the cleanup
526 # doesn't get called and we might end with a zombie vpp
527 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200528 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200529 cls.reporter.send_keep_alive(cls, 'setUpClass')
530 VppTestResult.current_test_case_info = TestCaseInfo(
531 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100532 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100533 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100534 cls.pump_thread_stop_flag = Event()
535 cls.pump_thread_wakeup_pipe = os.pipe()
536 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100537 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100538 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200539 if cls.debug_gdb or cls.debug_gdbserver:
540 read_timeout = 0
541 else:
542 read_timeout = 5
543 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
544 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100545 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400546 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100547 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400548 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100549 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200550 cls.wait_for_stats_socket()
551 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200552 try:
553 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100554 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200555 cls.vpp_startup_failed = True
556 cls.logger.critical(
557 "VPP died shortly after startup, check the"
558 " output to standard error for possible cause")
559 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100560 try:
561 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100562 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100563 try:
564 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100565 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100566 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100567 if cls.debug_gdbserver:
568 print(colorize("You're running VPP inside gdbserver but "
569 "VPP-API connection failed, did you forget "
570 "to 'continue' VPP from within gdb?", RED))
571 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000572 except Exception as e:
573 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400574
575 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100576 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200577
Damjan Marionf56b77a2016-10-03 19:44:57 +0200578 @classmethod
579 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200580 """
581 Disconnect vpp-api, kill vpp and cleanup shared memory files
582 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200583 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
584 cls.vpp.poll()
585 if cls.vpp.returncode is None:
586 print(double_line_delim)
587 print("VPP or GDB server is still running")
588 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800589 input("When done debugging, press ENTER to kill the "
590 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200591
juraj.linkes184870a2018-07-16 14:22:01 +0200592 # first signal that we want to stop the pump thread, then wake it up
593 if hasattr(cls, 'pump_thread_stop_flag'):
594 cls.pump_thread_stop_flag.set()
595 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100596 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100597 if hasattr(cls, 'pump_thread'):
598 cls.logger.debug("Waiting for pump thread to stop")
599 cls.pump_thread.join()
600 if hasattr(cls, 'vpp_stderr_reader_thread'):
601 cls.logger.debug("Waiting for stdderr pump to stop")
602 cls.vpp_stderr_reader_thread.join()
603
Klement Sekeraf62ae122016-10-11 11:47:09 +0200604 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100605 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700606 cls.logger.debug("Disconnecting class vapi client on %s",
607 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100608 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700609 cls.logger.debug("Deleting class vapi attribute on %s",
610 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100611 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200612 cls.vpp.poll()
613 if cls.vpp.returncode is None:
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400614 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100615 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400616 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100617 cls.logger.debug("Waiting for vpp to die")
618 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700619 cls.logger.debug("Deleting class vpp attribute on %s",
620 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200621 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200622
Klement Sekera3747c752017-04-10 06:30:17 +0200623 if cls.vpp_startup_failed:
624 stdout_log = cls.logger.info
625 stderr_log = cls.logger.critical
626 else:
627 stdout_log = cls.logger.info
628 stderr_log = cls.logger.info
629
Klement Sekerae4504c62016-12-08 10:16:41 +0100630 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200631 stdout_log(single_line_delim)
632 stdout_log('VPP output to stdout while running %s:', cls.__name__)
633 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100634 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200635 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
636 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200637 stdout_log('\n%s', vpp_output)
638 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200639
Klement Sekerae4504c62016-12-08 10:16:41 +0100640 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200641 stderr_log(single_line_delim)
642 stderr_log('VPP output to stderr while running %s:', cls.__name__)
643 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100644 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200645 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
646 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200647 stderr_log('\n%s', vpp_output)
648 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200649
Damjan Marionf56b77a2016-10-03 19:44:57 +0200650 @classmethod
651 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200652 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700653 cls.logger.debug("--- tearDownClass() for %s called ---" %
654 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200655 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200656 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200657 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100658 cls.reset_packet_infos()
659 if debug_framework:
660 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200661
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700662 def show_commands_at_teardown(self):
663 """ Allow subclass specific teardown logging additions."""
664 self.logger.info("--- No test specific show commands provided. ---")
665
Damjan Marionf56b77a2016-10-03 19:44:57 +0200666 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200667 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100668 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
669 (self.__class__.__name__, self._testMethodName,
670 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700671
672 try:
673 if not self.vpp_dead:
674 self.logger.debug(self.vapi.cli("show trace max 1000"))
675 self.logger.info(self.vapi.ppcli("show interface"))
676 self.logger.info(self.vapi.ppcli("show hardware"))
677 self.logger.info(self.statistics.set_errors_str())
678 self.logger.info(self.vapi.ppcli("show run"))
679 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400680 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700681 self.logger.info("Logging testcase specific show commands.")
682 self.show_commands_at_teardown()
683 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500684 # Save/Dump VPP api trace log
685 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
686 tmp_api_trace = "/tmp/%s" % api_trace
687 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
688 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
689 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
690 vpp_api_trace_log))
691 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500692 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500693 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700694 except VppTransportShmemIOError:
695 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
696 "Cannot log show commands.")
697 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100698 else:
699 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200700
Damjan Marionf56b77a2016-10-03 19:44:57 +0200701 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200702 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800703 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200704 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100705 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400706
707 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
708 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100709 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100710 self.vpp_stdout_deque.append(
711 "--- test setUp() for %s.%s(%s) starts here ---\n" %
712 (self.__class__.__name__, self._testMethodName,
713 self._testMethodDoc))
714 self.vpp_stderr_deque.append(
715 "--- test setUp() for %s.%s(%s) starts here ---\n" %
716 (self.__class__.__name__, self._testMethodName,
717 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200718 self.vapi.cli("clear trace")
719 # store the test instance inside the test class - so that objects
720 # holding the class can access instance methods (like assertEqual)
721 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200722
Damjan Marionf56b77a2016-10-03 19:44:57 +0200723 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200724 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200725 """
726 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727
Klement Sekera75e7d132017-09-20 08:26:30 +0200728 :param interfaces: iterable interface indexes (if None,
729 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200730
Klement Sekeraf62ae122016-10-11 11:47:09 +0200731 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200732 if interfaces is None:
733 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200734 for i in interfaces:
735 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736
Damjan Marionf56b77a2016-10-03 19:44:57 +0200737 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100738 def register_capture(cls, cap_name):
739 """ Register a capture in the testclass """
740 # add to the list of captures with current timestamp
741 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100742
743 @classmethod
744 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000745 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100746 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200747 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000748 # PG, when starts, runs to completion -
749 # so let's avoid a race condition,
750 # and wait a little till it's done.
751 # Then clean it up - and then be gone.
752 deadline = time.time() + 300
753 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
754 cls.sleep(0.01) # yield
755 if time.time() > deadline:
756 cls.logger.error("Timeout waiting for pg to stop")
757 break
758 for stamp, cap_name in cls._captures:
759 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100760 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200761
Damjan Marionf56b77a2016-10-03 19:44:57 +0200762 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200763 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200764 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100765 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200766
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100767 :param interfaces: iterable indexes of the interfaces.
768 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769
Klement Sekeraf62ae122016-10-11 11:47:09 +0200770 """
771 result = []
772 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200773 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200774 setattr(cls, intf.name, intf)
775 result.append(intf)
776 cls.pg_interfaces = result
777 return result
778
Matej Klotton0178d522016-11-04 11:11:44 +0100779 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200780 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100781 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100782 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100783
Klement Sekerab9ef2732018-06-24 22:49:33 +0200784 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100785 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100786 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200787 result = [VppLoInterface(cls) for i in range(count)]
788 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100789 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100790 cls.lo_interfaces = result
791 return result
792
Neale Ranns192b13f2019-03-15 02:16:20 -0700793 @classmethod
794 def create_bvi_interfaces(cls, count):
795 """
796 Create BVI interfaces.
797
798 :param count: number of interfaces created.
799 :returns: List of created interfaces.
800 """
801 result = [VppBviInterface(cls) for i in range(count)]
802 for intf in result:
803 setattr(cls, intf.name, intf)
804 cls.bvi_interfaces = result
805 return result
806
Damjan Marionf56b77a2016-10-03 19:44:57 +0200807 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200808 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200809 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200810 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200811 NOTE: Currently works only when Raw layer is present.
812
813 :param packet: packet
814 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200815 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200816
817 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200818 packet_len = len(packet) + 4
819 extend = size - packet_len
820 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200821 num = (extend // len(padding)) + 1
822 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200823
Klement Sekeradab231a2016-12-21 08:50:14 +0100824 @classmethod
825 def reset_packet_infos(cls):
826 """ Reset the list of packet info objects and packet counts to zero """
827 cls._packet_infos = {}
828 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200829
Klement Sekeradab231a2016-12-21 08:50:14 +0100830 @classmethod
831 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200832 """
833 Create packet info object containing the source and destination indexes
834 and add it to the testcase's packet info list
835
Klement Sekeradab231a2016-12-21 08:50:14 +0100836 :param VppInterface src_if: source interface
837 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200838
839 :returns: _PacketInfo object
840
841 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200842 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100843 info.index = len(cls._packet_infos)
844 info.src = src_if.sw_if_index
845 info.dst = dst_if.sw_if_index
846 if isinstance(dst_if, VppSubInterface):
847 dst_idx = dst_if.parent.sw_if_index
848 else:
849 dst_idx = dst_if.sw_if_index
850 if dst_idx in cls._packet_count_for_dst_if_idx:
851 cls._packet_count_for_dst_if_idx[dst_idx] += 1
852 else:
853 cls._packet_count_for_dst_if_idx[dst_idx] = 1
854 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200855 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200856
Damjan Marionf56b77a2016-10-03 19:44:57 +0200857 @staticmethod
858 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200859 """
860 Convert _PacketInfo object to packet payload
861
862 :param info: _PacketInfo object
863
864 :returns: string containing serialized data from packet info
865 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100866 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
867 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200868
Damjan Marionf56b77a2016-10-03 19:44:57 +0200869 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800870 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200871 """
872 Convert packet payload to _PacketInfo object
873
874 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700875 :type payload: <class 'scapy.packet.Raw'>
876 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800877 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700878 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200879 :returns: _PacketInfo object containing de-serialized data from payload
880
881 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800882 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200883 info = _PacketInfo()
884 info.index = int(numbers[0])
885 info.src = int(numbers[1])
886 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100887 info.ip = int(numbers[3])
888 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200889 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200890
Damjan Marionf56b77a2016-10-03 19:44:57 +0200891 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200892 """
893 Iterate over the packet info list stored in the testcase
894 Start iteration with first element if info is None
895 Continue based on index in info if info is specified
896
897 :param info: info or None
898 :returns: next info in list or None if no more infos
899 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200900 if info is None:
901 next_index = 0
902 else:
903 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100904 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200905 return None
906 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100907 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200908
Klement Sekeraf62ae122016-10-11 11:47:09 +0200909 def get_next_packet_info_for_interface(self, src_index, info):
910 """
911 Search the packet info list for the next packet info with same source
912 interface index
913
914 :param src_index: source interface index to search for
915 :param info: packet info - where to start the search
916 :returns: packet info or None
917
918 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200919 while True:
920 info = self.get_next_packet_info(info)
921 if info is None:
922 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200923 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200924 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200925
Klement Sekeraf62ae122016-10-11 11:47:09 +0200926 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
927 """
928 Search the packet info list for the next packet info with same source
929 and destination interface indexes
930
931 :param src_index: source interface index to search for
932 :param dst_index: destination interface index to search for
933 :param info: packet info - where to start the search
934 :returns: packet info or None
935
936 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200937 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200938 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200939 if info is None:
940 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200941 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200942 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200943
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200944 def assert_equal(self, real_value, expected_value, name_or_class=None):
945 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100946 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200947 return
948 try:
949 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
950 msg = msg % (getdoc(name_or_class).strip(),
951 real_value, str(name_or_class(real_value)),
952 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100953 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200954 msg = "Invalid %s: %s does not match expected value %s" % (
955 name_or_class, real_value, expected_value)
956
957 self.assertEqual(real_value, expected_value, msg)
958
Klement Sekerab17dd962017-01-09 07:43:48 +0100959 def assert_in_range(self,
960 real_value,
961 expected_min,
962 expected_max,
963 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200964 if name is None:
965 msg = None
966 else:
967 msg = "Invalid %s: %s out of range <%s,%s>" % (
968 name, real_value, expected_min, expected_max)
969 self.assertTrue(expected_min <= real_value <= expected_max, msg)
970
Klement Sekerad81ae412018-05-16 10:52:54 +0200971 def assert_packet_checksums_valid(self, packet,
972 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700973 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200974 udp_layers = ['UDP', 'UDPerror']
975 checksum_fields = ['cksum', 'chksum']
976 checksums = []
977 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700978 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200979 while True:
980 layer = temp.getlayer(counter)
981 if layer:
982 for cf in checksum_fields:
983 if hasattr(layer, cf):
984 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +0200985 0 == getattr(layer, cf) and \
986 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200987 continue
988 delattr(layer, cf)
989 checksums.append((counter, cf))
990 else:
991 break
992 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200993 if 0 == len(checksums):
994 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700995 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200996 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200997 calc_sum = getattr(temp[layer], cf)
998 self.assert_equal(
999 getattr(received[layer], cf), calc_sum,
1000 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1001 self.logger.debug(
1002 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1003 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001004
1005 def assert_checksum_valid(self, received_packet, layer,
1006 field_name='chksum',
1007 ignore_zero_checksum=False):
1008 """ Check checksum of received packet on given layer """
1009 received_packet_checksum = getattr(received_packet[layer], field_name)
1010 if ignore_zero_checksum and 0 == received_packet_checksum:
1011 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001012 recalculated = received_packet.__class__(
1013 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001014 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001015 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001016 self.assert_equal(received_packet_checksum,
1017 getattr(recalculated[layer], field_name),
1018 "packet checksum on layer: %s" % layer)
1019
1020 def assert_ip_checksum_valid(self, received_packet,
1021 ignore_zero_checksum=False):
1022 self.assert_checksum_valid(received_packet, 'IP',
1023 ignore_zero_checksum=ignore_zero_checksum)
1024
1025 def assert_tcp_checksum_valid(self, received_packet,
1026 ignore_zero_checksum=False):
1027 self.assert_checksum_valid(received_packet, 'TCP',
1028 ignore_zero_checksum=ignore_zero_checksum)
1029
1030 def assert_udp_checksum_valid(self, received_packet,
1031 ignore_zero_checksum=True):
1032 self.assert_checksum_valid(received_packet, 'UDP',
1033 ignore_zero_checksum=ignore_zero_checksum)
1034
1035 def assert_embedded_icmp_checksum_valid(self, received_packet):
1036 if received_packet.haslayer(IPerror):
1037 self.assert_checksum_valid(received_packet, 'IPerror')
1038 if received_packet.haslayer(TCPerror):
1039 self.assert_checksum_valid(received_packet, 'TCPerror')
1040 if received_packet.haslayer(UDPerror):
1041 self.assert_checksum_valid(received_packet, 'UDPerror',
1042 ignore_zero_checksum=True)
1043 if received_packet.haslayer(ICMPerror):
1044 self.assert_checksum_valid(received_packet, 'ICMPerror')
1045
1046 def assert_icmp_checksum_valid(self, received_packet):
1047 self.assert_checksum_valid(received_packet, 'ICMP')
1048 self.assert_embedded_icmp_checksum_valid(received_packet)
1049
1050 def assert_icmpv6_checksum_valid(self, pkt):
1051 if pkt.haslayer(ICMPv6DestUnreach):
1052 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1053 self.assert_embedded_icmp_checksum_valid(pkt)
1054 if pkt.haslayer(ICMPv6EchoRequest):
1055 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1056 if pkt.haslayer(ICMPv6EchoReply):
1057 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1058
Klement Sekera3a343d42019-05-16 14:35:46 +02001059 def get_packet_counter(self, counter):
1060 if counter.startswith("/"):
1061 counter_value = self.statistics.get_counter(counter)
1062 else:
1063 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001064 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001065 for i in range(1, len(counters) - 1):
1066 results = counters[i].split()
1067 if results[1] == counter:
1068 counter_value = int(results[0])
1069 break
1070 return counter_value
1071
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001072 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001073 counter_value = self.get_packet_counter(counter)
1074 self.assert_equal(counter_value, expected_value,
1075 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001076
Ole Troan233e4682019-05-16 15:01:34 +02001077 def assert_error_counter_equal(self, counter, expected_value):
1078 counter_value = self.statistics.get_err_counter(counter)
1079 self.assert_equal(counter_value, expected_value,
1080 "error counter `%s'" % counter)
1081
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001082 @classmethod
1083 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001084
1085 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1086 # * by Guido, only the main thread can be interrupted.
1087 # */
1088 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1089 if timeout == 0:
1090 # yield quantum
1091 if hasattr(os, 'sched_yield'):
1092 os.sched_yield()
1093 else:
1094 time.sleep(0)
1095 return
1096
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001097 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001098 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001099 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001100 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001101 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001102 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001103 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001104 "slept for %es instead of ~%es!",
1105 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001106 if hasattr(cls, 'logger'):
1107 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001108 "Finished sleep (%s) - slept %es (wanted %es)",
1109 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001110
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001111 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001112 self.vapi.cli("clear trace")
1113 intf.add_stream(pkts)
1114 self.pg_enable_capture(self.pg_interfaces)
1115 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001116
1117 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1118 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001119 if not timeout:
1120 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001121 for i in self.pg_interfaces:
1122 i.get_capture(0, timeout=timeout)
1123 i.assert_nothing_captured(remark=remark)
1124 timeout = 0.1
1125
Neale Rannsd7603d92019-03-28 08:56:10 +00001126 def send_and_expect(self, intf, pkts, output, n_rx=None):
1127 if not n_rx:
1128 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001129 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001130 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001131 return rx
1132
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001133 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1134 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001135 rx = output.get_capture(len(pkts))
1136 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001137 if not timeout:
1138 timeout = 1
1139 for i in self.pg_interfaces:
1140 if i not in outputs:
1141 i.get_capture(0, timeout=timeout)
1142 i.assert_nothing_captured()
1143 timeout = 0.1
1144
Neale Ranns52fae862018-01-08 04:41:42 -08001145 return rx
1146
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001147 def runTest(self):
1148 """ unittest calls runTest when TestCase is instantiated without a
1149 test case. Use case: Writing unittests against VppTestCase"""
1150 pass
1151
Damjan Marionf56b77a2016-10-03 19:44:57 +02001152
juraj.linkes184870a2018-07-16 14:22:01 +02001153def get_testcase_doc_name(test):
1154 return getdoc(test.__class__).splitlines()[0]
1155
1156
Ole Trøan5ba91592018-11-22 10:01:09 +00001157def get_test_description(descriptions, test):
1158 short_description = test.shortDescription()
1159 if descriptions and short_description:
1160 return short_description
1161 else:
1162 return str(test)
1163
1164
juraj.linkes40dd73b2018-09-21 13:55:16 +02001165class TestCaseInfo(object):
1166 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1167 self.logger = logger
1168 self.tempdir = tempdir
1169 self.vpp_pid = vpp_pid
1170 self.vpp_bin_path = vpp_bin_path
1171 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001172
1173
Damjan Marionf56b77a2016-10-03 19:44:57 +02001174class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001175 """
1176 @property result_string
1177 String variable to store the test case result string.
1178 @property errors
1179 List variable containing 2-tuples of TestCase instances and strings
1180 holding formatted tracebacks. Each tuple represents a test which
1181 raised an unexpected exception.
1182 @property failures
1183 List variable containing 2-tuples of TestCase instances and strings
1184 holding formatted tracebacks. Each tuple represents a test where
1185 a failure was explicitly signalled using the TestCase.assert*()
1186 methods.
1187 """
1188
juraj.linkes40dd73b2018-09-21 13:55:16 +02001189 failed_test_cases_info = set()
1190 core_crash_test_cases_info = set()
1191 current_test_case_info = None
1192
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001193 def __init__(self, stream=None, descriptions=None, verbosity=None,
1194 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001195 """
Klement Sekerada505f62017-01-04 12:58:53 +01001196 :param stream File descriptor to store where to report test results.
1197 Set to the standard error stream by default.
1198 :param descriptions Boolean variable to store information if to use
1199 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001200 :param verbosity Integer variable to store required verbosity level.
1201 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001202 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001203 self.stream = stream
1204 self.descriptions = descriptions
1205 self.verbosity = verbosity
1206 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001207 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001208
Damjan Marionf56b77a2016-10-03 19:44:57 +02001209 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001210 """
1211 Record a test succeeded result
1212
1213 :param test:
1214
1215 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001216 if self.current_test_case_info:
1217 self.current_test_case_info.logger.debug(
1218 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1219 test._testMethodName,
1220 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001221 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001222 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001223
juraj.linkescae64f82018-09-19 15:01:47 +02001224 self.send_result_through_pipe(test, PASS)
1225
Klement Sekeraf62ae122016-10-11 11:47:09 +02001226 def addSkip(self, test, reason):
1227 """
1228 Record a test skipped.
1229
1230 :param test:
1231 :param reason:
1232
1233 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001234 if self.current_test_case_info:
1235 self.current_test_case_info.logger.debug(
1236 "--- addSkip() %s.%s(%s) called, reason is %s" %
1237 (test.__class__.__name__, test._testMethodName,
1238 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001239 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001240 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001241
juraj.linkescae64f82018-09-19 15:01:47 +02001242 self.send_result_through_pipe(test, SKIP)
1243
juraj.linkes40dd73b2018-09-21 13:55:16 +02001244 def symlink_failed(self):
1245 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001246 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001247 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001248 link_path = os.path.join(
1249 failed_dir,
1250 '%s-FAILED' %
1251 os.path.basename(self.current_test_case_info.tempdir))
1252 if self.current_test_case_info.logger:
1253 self.current_test_case_info.logger.debug(
1254 "creating a link to the failed test")
1255 self.current_test_case_info.logger.debug(
1256 "os.symlink(%s, %s)" %
1257 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001258 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001259 if self.current_test_case_info.logger:
1260 self.current_test_case_info.logger.debug(
1261 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001262 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001263 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001264
Klement Sekeraf413bef2017-08-15 07:09:02 +02001265 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001266 if self.current_test_case_info.logger:
1267 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001268
juraj.linkescae64f82018-09-19 15:01:47 +02001269 def send_result_through_pipe(self, test, result):
1270 if hasattr(self, 'test_framework_result_pipe'):
1271 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001272 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001273 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001274
juraj.linkes40dd73b2018-09-21 13:55:16 +02001275 def log_error(self, test, err, fn_name):
1276 if self.current_test_case_info:
1277 if isinstance(test, unittest.suite._ErrorHolder):
1278 test_name = test.description
1279 else:
1280 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1281 test._testMethodName,
1282 test._testMethodDoc)
1283 self.current_test_case_info.logger.debug(
1284 "--- %s() %s called, err is %s" %
1285 (fn_name, test_name, err))
1286 self.current_test_case_info.logger.debug(
1287 "formatted exception is:\n%s" %
1288 "".join(format_exception(*err)))
1289
1290 def add_error(self, test, err, unittest_fn, error_type):
1291 if error_type == FAIL:
1292 self.log_error(test, err, 'addFailure')
1293 error_type_str = colorize("FAIL", RED)
1294 elif error_type == ERROR:
1295 self.log_error(test, err, 'addError')
1296 error_type_str = colorize("ERROR", RED)
1297 else:
1298 raise Exception('Error type %s cannot be used to record an '
1299 'error or a failure' % error_type)
1300
1301 unittest_fn(self, test, err)
1302 if self.current_test_case_info:
1303 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1304 (error_type_str,
1305 self.current_test_case_info.tempdir)
1306 self.symlink_failed()
1307 self.failed_test_cases_info.add(self.current_test_case_info)
1308 if is_core_present(self.current_test_case_info.tempdir):
1309 if not self.current_test_case_info.core_crash_test:
1310 if isinstance(test, unittest.suite._ErrorHolder):
1311 test_name = str(test)
1312 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001313 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001314 get_testcase_doc_name(test), test.id())
1315 self.current_test_case_info.core_crash_test = test_name
1316 self.core_crash_test_cases_info.add(
1317 self.current_test_case_info)
1318 else:
1319 self.result_string = '%s [no temp dir]' % error_type_str
1320
1321 self.send_result_through_pipe(test, error_type)
1322
Damjan Marionf56b77a2016-10-03 19:44:57 +02001323 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001324 """
1325 Record a test failed result
1326
1327 :param test:
1328 :param err: error message
1329
1330 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001331 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001332
Damjan Marionf56b77a2016-10-03 19:44:57 +02001333 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001334 """
1335 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001336
Klement Sekeraf62ae122016-10-11 11:47:09 +02001337 :param test:
1338 :param err: error message
1339
1340 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001341 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001342
Damjan Marionf56b77a2016-10-03 19:44:57 +02001343 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001344 """
1345 Get test description
1346
1347 :param test:
1348 :returns: test description
1349
1350 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001351 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001352
Damjan Marionf56b77a2016-10-03 19:44:57 +02001353 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001354 """
1355 Start a test
1356
1357 :param test:
1358
1359 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001360
1361 def print_header(test):
1362 if not hasattr(test.__class__, '_header_printed'):
1363 print(double_line_delim)
1364 print(colorize(getdoc(test).splitlines()[0], GREEN))
1365 print(double_line_delim)
1366 test.__class__._header_printed = True
1367
1368 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001369
Damjan Marionf56b77a2016-10-03 19:44:57 +02001370 unittest.TestResult.startTest(self, test)
1371 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001372 self.stream.writeln(
1373 "Starting " + self.getDescription(test) + " ...")
1374 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001375
Damjan Marionf56b77a2016-10-03 19:44:57 +02001376 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001377 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001378 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001379
1380 :param test:
1381
1382 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001383 unittest.TestResult.stopTest(self, test)
1384 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001385 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001386 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001387 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001388 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001389 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001390 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001391 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001392
1393 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001394
Damjan Marionf56b77a2016-10-03 19:44:57 +02001395 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001396 """
1397 Print errors from running the test case
1398 """
juraj.linkesabec0122018-11-16 17:28:56 +01001399 if len(self.errors) > 0 or len(self.failures) > 0:
1400 self.stream.writeln()
1401 self.printErrorList('ERROR', self.errors)
1402 self.printErrorList('FAIL', self.failures)
1403
1404 # ^^ that is the last output from unittest before summary
1405 if not self.runner.print_summary:
1406 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1407 self.stream = devnull
1408 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001409
Damjan Marionf56b77a2016-10-03 19:44:57 +02001410 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001411 """
1412 Print error list to the output stream together with error type
1413 and test case description.
1414
1415 :param flavour: error type
1416 :param errors: iterable errors
1417
1418 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001419 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001420 self.stream.writeln(double_line_delim)
1421 self.stream.writeln("%s: %s" %
1422 (flavour, self.getDescription(test)))
1423 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001424 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001425
1426
Damjan Marionf56b77a2016-10-03 19:44:57 +02001427class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001428 """
Klement Sekera104543f2017-02-03 07:29:43 +01001429 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001430 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001431
Klement Sekeraf62ae122016-10-11 11:47:09 +02001432 @property
1433 def resultclass(self):
1434 """Class maintaining the results of the tests"""
1435 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001436
juraj.linkes184870a2018-07-16 14:22:01 +02001437 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001438 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001439 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001440 # ignore stream setting here, use hard-coded stdout to be in sync
1441 # with prints from VppTestCase methods ...
1442 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1443 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001444 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001445 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001446
juraj.linkesabec0122018-11-16 17:28:56 +01001447 self.orig_stream = self.stream
1448 self.resultclass.test_framework_result_pipe = result_pipe
1449
1450 self.print_summary = print_summary
1451
1452 def _makeResult(self):
1453 return self.resultclass(self.stream,
1454 self.descriptions,
1455 self.verbosity,
1456 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001457
Damjan Marionf56b77a2016-10-03 19:44:57 +02001458 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001459 """
1460 Run the tests
1461
1462 :param test:
1463
1464 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001465 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001466
1467 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001468 if not self.print_summary:
1469 self.stream = self.orig_stream
1470 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001471 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001472
1473
1474class Worker(Thread):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001475 def __init__(self, args, logger, env=None):
Neale Ranns812ed392017-10-16 04:20:13 -07001476 self.logger = logger
1477 self.args = args
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001478 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001479 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001480 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001481 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001482 super(Worker, self).__init__()
1483
1484 def run(self):
1485 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001486 if not os.path.exists(executable) or not os.access(
1487 executable, os.F_OK | os.X_OK):
1488 # Exit code that means some system file did not exist,
1489 # could not be opened, or had some other kind of error.
1490 self.result = os.EX_OSFILE
1491 raise EnvironmentError(
1492 "executable '%s' is not found or executable." % executable)
Neale Ranns812ed392017-10-16 04:20:13 -07001493 self.logger.debug("Running executable w/args `%s'" % self.args)
1494 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001495 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001496 env["CK_LOG_FILE_NAME"] = "-"
1497 self.process = subprocess.Popen(
1498 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1499 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1500 out, err = self.process.communicate()
1501 self.logger.debug("Finished running `%s'" % executable)
1502 self.logger.info("Return code is `%s'" % self.process.returncode)
1503 self.logger.info(single_line_delim)
1504 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1505 self.logger.info(single_line_delim)
1506 self.logger.info(out)
1507 self.logger.info(single_line_delim)
1508 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1509 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001510 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001511 self.logger.info(single_line_delim)
1512 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001513
Klement Sekera6aa58b72019-05-16 14:34:55 +02001514
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001515if __name__ == '__main__':
1516 pass