blob: 307da8f22f247dd25bdc0740b72f82d0cb42fae9 [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
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800221running_on_centos = _running_on_centos
222
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):
454 deadline = time.time() + 3
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 = []
520 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200521 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100522 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100523 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200524 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200525 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200526 # need to catch exceptions here because if we raise, then the cleanup
527 # doesn't get called and we might end with a zombie vpp
528 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200529 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200530 cls.reporter.send_keep_alive(cls, 'setUpClass')
531 VppTestResult.current_test_case_info = TestCaseInfo(
532 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100533 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100534 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100535 cls.pump_thread_stop_flag = Event()
536 cls.pump_thread_wakeup_pipe = os.pipe()
537 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100538 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100539 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200540 if cls.debug_gdb or cls.debug_gdbserver:
541 read_timeout = 0
542 else:
543 read_timeout = 5
544 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
545 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100546 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400547 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100548 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400549 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100550 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200551 cls.wait_for_stats_socket()
552 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200553 try:
554 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100555 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200556 cls.vpp_startup_failed = True
557 cls.logger.critical(
558 "VPP died shortly after startup, check the"
559 " output to standard error for possible cause")
560 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100561 try:
562 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100563 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100564 try:
565 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100566 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100567 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100568 if cls.debug_gdbserver:
569 print(colorize("You're running VPP inside gdbserver but "
570 "VPP-API connection failed, did you forget "
571 "to 'continue' VPP from within gdb?", RED))
572 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100573 except Exception:
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))
742 # filter out from zombies
743 cls._zombie_captures = [(stamp, name)
744 for (stamp, name) in cls._zombie_captures
745 if name != cap_name]
746
747 @classmethod
748 def pg_start(cls):
749 """ Remove any zombie captures and enable the packet generator """
750 # how long before capture is allowed to be deleted - otherwise vpp
751 # crashes - 100ms seems enough (this shouldn't be needed at all)
752 capture_ttl = 0.1
753 now = time.time()
754 for stamp, cap_name in cls._zombie_captures:
755 wait = stamp + capture_ttl - now
756 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100757 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100758 now = time.time()
759 cls.logger.debug("Removing zombie capture %s" % cap_name)
760 cls.vapi.cli('packet-generator delete %s' % cap_name)
761
Klement Sekerad91fa612019-01-15 13:25:09 +0100762 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200763 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100764 cls._zombie_captures = cls._captures
765 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200766
Damjan Marionf56b77a2016-10-03 19:44:57 +0200767 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200768 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200769 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100770 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200771
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100772 :param interfaces: iterable indexes of the interfaces.
773 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200774
Klement Sekeraf62ae122016-10-11 11:47:09 +0200775 """
776 result = []
777 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200778 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200779 setattr(cls, intf.name, intf)
780 result.append(intf)
781 cls.pg_interfaces = result
782 return result
783
Matej Klotton0178d522016-11-04 11:11:44 +0100784 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200785 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100786 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100787 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100788
Klement Sekerab9ef2732018-06-24 22:49:33 +0200789 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100790 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100791 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200792 result = [VppLoInterface(cls) for i in range(count)]
793 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100794 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100795 cls.lo_interfaces = result
796 return result
797
Neale Ranns192b13f2019-03-15 02:16:20 -0700798 @classmethod
799 def create_bvi_interfaces(cls, count):
800 """
801 Create BVI interfaces.
802
803 :param count: number of interfaces created.
804 :returns: List of created interfaces.
805 """
806 result = [VppBviInterface(cls) for i in range(count)]
807 for intf in result:
808 setattr(cls, intf.name, intf)
809 cls.bvi_interfaces = result
810 return result
811
Damjan Marionf56b77a2016-10-03 19:44:57 +0200812 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200813 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200814 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200815 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200816 NOTE: Currently works only when Raw layer is present.
817
818 :param packet: packet
819 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200820 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200821
822 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200823 packet_len = len(packet) + 4
824 extend = size - packet_len
825 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200826 num = (extend // len(padding)) + 1
827 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200828
Klement Sekeradab231a2016-12-21 08:50:14 +0100829 @classmethod
830 def reset_packet_infos(cls):
831 """ Reset the list of packet info objects and packet counts to zero """
832 cls._packet_infos = {}
833 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200834
Klement Sekeradab231a2016-12-21 08:50:14 +0100835 @classmethod
836 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200837 """
838 Create packet info object containing the source and destination indexes
839 and add it to the testcase's packet info list
840
Klement Sekeradab231a2016-12-21 08:50:14 +0100841 :param VppInterface src_if: source interface
842 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200843
844 :returns: _PacketInfo object
845
846 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200847 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100848 info.index = len(cls._packet_infos)
849 info.src = src_if.sw_if_index
850 info.dst = dst_if.sw_if_index
851 if isinstance(dst_if, VppSubInterface):
852 dst_idx = dst_if.parent.sw_if_index
853 else:
854 dst_idx = dst_if.sw_if_index
855 if dst_idx in cls._packet_count_for_dst_if_idx:
856 cls._packet_count_for_dst_if_idx[dst_idx] += 1
857 else:
858 cls._packet_count_for_dst_if_idx[dst_idx] = 1
859 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200860 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200861
Damjan Marionf56b77a2016-10-03 19:44:57 +0200862 @staticmethod
863 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200864 """
865 Convert _PacketInfo object to packet payload
866
867 :param info: _PacketInfo object
868
869 :returns: string containing serialized data from packet info
870 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100871 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
872 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200873
Damjan Marionf56b77a2016-10-03 19:44:57 +0200874 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800875 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200876 """
877 Convert packet payload to _PacketInfo object
878
879 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700880 :type payload: <class 'scapy.packet.Raw'>
881 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800882 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700883 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200884 :returns: _PacketInfo object containing de-serialized data from payload
885
886 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800887 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200888 info = _PacketInfo()
889 info.index = int(numbers[0])
890 info.src = int(numbers[1])
891 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100892 info.ip = int(numbers[3])
893 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200894 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200895
Damjan Marionf56b77a2016-10-03 19:44:57 +0200896 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200897 """
898 Iterate over the packet info list stored in the testcase
899 Start iteration with first element if info is None
900 Continue based on index in info if info is specified
901
902 :param info: info or None
903 :returns: next info in list or None if no more infos
904 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200905 if info is None:
906 next_index = 0
907 else:
908 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100909 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200910 return None
911 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100912 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200913
Klement Sekeraf62ae122016-10-11 11:47:09 +0200914 def get_next_packet_info_for_interface(self, src_index, info):
915 """
916 Search the packet info list for the next packet info with same source
917 interface index
918
919 :param src_index: source interface index to search for
920 :param info: packet info - where to start the search
921 :returns: packet info or None
922
923 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200924 while True:
925 info = self.get_next_packet_info(info)
926 if info is None:
927 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200928 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200929 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200930
Klement Sekeraf62ae122016-10-11 11:47:09 +0200931 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
932 """
933 Search the packet info list for the next packet info with same source
934 and destination interface indexes
935
936 :param src_index: source interface index to search for
937 :param dst_index: destination interface index to search for
938 :param info: packet info - where to start the search
939 :returns: packet info or None
940
941 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200942 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200943 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200944 if info is None:
945 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200946 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200947 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200948
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200949 def assert_equal(self, real_value, expected_value, name_or_class=None):
950 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100951 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200952 return
953 try:
954 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
955 msg = msg % (getdoc(name_or_class).strip(),
956 real_value, str(name_or_class(real_value)),
957 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100958 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200959 msg = "Invalid %s: %s does not match expected value %s" % (
960 name_or_class, real_value, expected_value)
961
962 self.assertEqual(real_value, expected_value, msg)
963
Klement Sekerab17dd962017-01-09 07:43:48 +0100964 def assert_in_range(self,
965 real_value,
966 expected_min,
967 expected_max,
968 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200969 if name is None:
970 msg = None
971 else:
972 msg = "Invalid %s: %s out of range <%s,%s>" % (
973 name, real_value, expected_min, expected_max)
974 self.assertTrue(expected_min <= real_value <= expected_max, msg)
975
Klement Sekerad81ae412018-05-16 10:52:54 +0200976 def assert_packet_checksums_valid(self, packet,
977 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700978 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200979 udp_layers = ['UDP', 'UDPerror']
980 checksum_fields = ['cksum', 'chksum']
981 checksums = []
982 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700983 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200984 while True:
985 layer = temp.getlayer(counter)
986 if layer:
987 for cf in checksum_fields:
988 if hasattr(layer, cf):
989 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +0200990 0 == getattr(layer, cf) and \
991 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200992 continue
993 delattr(layer, cf)
994 checksums.append((counter, cf))
995 else:
996 break
997 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200998 if 0 == len(checksums):
999 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001000 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001001 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001002 calc_sum = getattr(temp[layer], cf)
1003 self.assert_equal(
1004 getattr(received[layer], cf), calc_sum,
1005 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1006 self.logger.debug(
1007 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1008 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001009
1010 def assert_checksum_valid(self, received_packet, layer,
1011 field_name='chksum',
1012 ignore_zero_checksum=False):
1013 """ Check checksum of received packet on given layer """
1014 received_packet_checksum = getattr(received_packet[layer], field_name)
1015 if ignore_zero_checksum and 0 == received_packet_checksum:
1016 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001017 recalculated = received_packet.__class__(
1018 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001019 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001020 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001021 self.assert_equal(received_packet_checksum,
1022 getattr(recalculated[layer], field_name),
1023 "packet checksum on layer: %s" % layer)
1024
1025 def assert_ip_checksum_valid(self, received_packet,
1026 ignore_zero_checksum=False):
1027 self.assert_checksum_valid(received_packet, 'IP',
1028 ignore_zero_checksum=ignore_zero_checksum)
1029
1030 def assert_tcp_checksum_valid(self, received_packet,
1031 ignore_zero_checksum=False):
1032 self.assert_checksum_valid(received_packet, 'TCP',
1033 ignore_zero_checksum=ignore_zero_checksum)
1034
1035 def assert_udp_checksum_valid(self, received_packet,
1036 ignore_zero_checksum=True):
1037 self.assert_checksum_valid(received_packet, 'UDP',
1038 ignore_zero_checksum=ignore_zero_checksum)
1039
1040 def assert_embedded_icmp_checksum_valid(self, received_packet):
1041 if received_packet.haslayer(IPerror):
1042 self.assert_checksum_valid(received_packet, 'IPerror')
1043 if received_packet.haslayer(TCPerror):
1044 self.assert_checksum_valid(received_packet, 'TCPerror')
1045 if received_packet.haslayer(UDPerror):
1046 self.assert_checksum_valid(received_packet, 'UDPerror',
1047 ignore_zero_checksum=True)
1048 if received_packet.haslayer(ICMPerror):
1049 self.assert_checksum_valid(received_packet, 'ICMPerror')
1050
1051 def assert_icmp_checksum_valid(self, received_packet):
1052 self.assert_checksum_valid(received_packet, 'ICMP')
1053 self.assert_embedded_icmp_checksum_valid(received_packet)
1054
1055 def assert_icmpv6_checksum_valid(self, pkt):
1056 if pkt.haslayer(ICMPv6DestUnreach):
1057 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1058 self.assert_embedded_icmp_checksum_valid(pkt)
1059 if pkt.haslayer(ICMPv6EchoRequest):
1060 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1061 if pkt.haslayer(ICMPv6EchoReply):
1062 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1063
Klement Sekera3a343d42019-05-16 14:35:46 +02001064 def get_packet_counter(self, counter):
1065 if counter.startswith("/"):
1066 counter_value = self.statistics.get_counter(counter)
1067 else:
1068 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001069 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001070 for i in range(1, len(counters) - 1):
1071 results = counters[i].split()
1072 if results[1] == counter:
1073 counter_value = int(results[0])
1074 break
1075 return counter_value
1076
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001077 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001078 counter_value = self.get_packet_counter(counter)
1079 self.assert_equal(counter_value, expected_value,
1080 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001081
Ole Troan233e4682019-05-16 15:01:34 +02001082 def assert_error_counter_equal(self, counter, expected_value):
1083 counter_value = self.statistics.get_err_counter(counter)
1084 self.assert_equal(counter_value, expected_value,
1085 "error counter `%s'" % counter)
1086
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001087 @classmethod
1088 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001089
1090 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1091 # * by Guido, only the main thread can be interrupted.
1092 # */
1093 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1094 if timeout == 0:
1095 # yield quantum
1096 if hasattr(os, 'sched_yield'):
1097 os.sched_yield()
1098 else:
1099 time.sleep(0)
1100 return
1101
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001102 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001103 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001104 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001105 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001106 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001107 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001108 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001109 "slept for %es instead of ~%es!",
1110 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001111 if hasattr(cls, 'logger'):
1112 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001113 "Finished sleep (%s) - slept %es (wanted %es)",
1114 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001115
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001116 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001117 self.vapi.cli("clear trace")
1118 intf.add_stream(pkts)
1119 self.pg_enable_capture(self.pg_interfaces)
1120 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001121
1122 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1123 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001124 if not timeout:
1125 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001126 for i in self.pg_interfaces:
1127 i.get_capture(0, timeout=timeout)
1128 i.assert_nothing_captured(remark=remark)
1129 timeout = 0.1
1130
Neale Rannsd7603d92019-03-28 08:56:10 +00001131 def send_and_expect(self, intf, pkts, output, n_rx=None):
1132 if not n_rx:
1133 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001134 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001135 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001136 return rx
1137
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001138 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1139 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001140 rx = output.get_capture(len(pkts))
1141 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001142 if not timeout:
1143 timeout = 1
1144 for i in self.pg_interfaces:
1145 if i not in outputs:
1146 i.get_capture(0, timeout=timeout)
1147 i.assert_nothing_captured()
1148 timeout = 0.1
1149
Neale Ranns52fae862018-01-08 04:41:42 -08001150 return rx
1151
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001152 def runTest(self):
1153 """ unittest calls runTest when TestCase is instantiated without a
1154 test case. Use case: Writing unittests against VppTestCase"""
1155 pass
1156
Damjan Marionf56b77a2016-10-03 19:44:57 +02001157
juraj.linkes184870a2018-07-16 14:22:01 +02001158def get_testcase_doc_name(test):
1159 return getdoc(test.__class__).splitlines()[0]
1160
1161
Ole Trøan5ba91592018-11-22 10:01:09 +00001162def get_test_description(descriptions, test):
1163 short_description = test.shortDescription()
1164 if descriptions and short_description:
1165 return short_description
1166 else:
1167 return str(test)
1168
1169
juraj.linkes40dd73b2018-09-21 13:55:16 +02001170class TestCaseInfo(object):
1171 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1172 self.logger = logger
1173 self.tempdir = tempdir
1174 self.vpp_pid = vpp_pid
1175 self.vpp_bin_path = vpp_bin_path
1176 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001177
1178
Damjan Marionf56b77a2016-10-03 19:44:57 +02001179class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001180 """
1181 @property result_string
1182 String variable to store the test case result string.
1183 @property errors
1184 List variable containing 2-tuples of TestCase instances and strings
1185 holding formatted tracebacks. Each tuple represents a test which
1186 raised an unexpected exception.
1187 @property failures
1188 List variable containing 2-tuples of TestCase instances and strings
1189 holding formatted tracebacks. Each tuple represents a test where
1190 a failure was explicitly signalled using the TestCase.assert*()
1191 methods.
1192 """
1193
juraj.linkes40dd73b2018-09-21 13:55:16 +02001194 failed_test_cases_info = set()
1195 core_crash_test_cases_info = set()
1196 current_test_case_info = None
1197
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001198 def __init__(self, stream=None, descriptions=None, verbosity=None,
1199 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001200 """
Klement Sekerada505f62017-01-04 12:58:53 +01001201 :param stream File descriptor to store where to report test results.
1202 Set to the standard error stream by default.
1203 :param descriptions Boolean variable to store information if to use
1204 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001205 :param verbosity Integer variable to store required verbosity level.
1206 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001207 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001208 self.stream = stream
1209 self.descriptions = descriptions
1210 self.verbosity = verbosity
1211 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001212 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001213
Damjan Marionf56b77a2016-10-03 19:44:57 +02001214 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001215 """
1216 Record a test succeeded result
1217
1218 :param test:
1219
1220 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001221 if self.current_test_case_info:
1222 self.current_test_case_info.logger.debug(
1223 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1224 test._testMethodName,
1225 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001226 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001227 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001228
juraj.linkescae64f82018-09-19 15:01:47 +02001229 self.send_result_through_pipe(test, PASS)
1230
Klement Sekeraf62ae122016-10-11 11:47:09 +02001231 def addSkip(self, test, reason):
1232 """
1233 Record a test skipped.
1234
1235 :param test:
1236 :param reason:
1237
1238 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001239 if self.current_test_case_info:
1240 self.current_test_case_info.logger.debug(
1241 "--- addSkip() %s.%s(%s) called, reason is %s" %
1242 (test.__class__.__name__, test._testMethodName,
1243 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001244 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001245 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001246
juraj.linkescae64f82018-09-19 15:01:47 +02001247 self.send_result_through_pipe(test, SKIP)
1248
juraj.linkes40dd73b2018-09-21 13:55:16 +02001249 def symlink_failed(self):
1250 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001251 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001252 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001253 link_path = os.path.join(
1254 failed_dir,
1255 '%s-FAILED' %
1256 os.path.basename(self.current_test_case_info.tempdir))
1257 if self.current_test_case_info.logger:
1258 self.current_test_case_info.logger.debug(
1259 "creating a link to the failed test")
1260 self.current_test_case_info.logger.debug(
1261 "os.symlink(%s, %s)" %
1262 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001263 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001264 if self.current_test_case_info.logger:
1265 self.current_test_case_info.logger.debug(
1266 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001267 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001268 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001269
Klement Sekeraf413bef2017-08-15 07:09:02 +02001270 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001271 if self.current_test_case_info.logger:
1272 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001273
juraj.linkescae64f82018-09-19 15:01:47 +02001274 def send_result_through_pipe(self, test, result):
1275 if hasattr(self, 'test_framework_result_pipe'):
1276 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001277 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001278 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001279
juraj.linkes40dd73b2018-09-21 13:55:16 +02001280 def log_error(self, test, err, fn_name):
1281 if self.current_test_case_info:
1282 if isinstance(test, unittest.suite._ErrorHolder):
1283 test_name = test.description
1284 else:
1285 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1286 test._testMethodName,
1287 test._testMethodDoc)
1288 self.current_test_case_info.logger.debug(
1289 "--- %s() %s called, err is %s" %
1290 (fn_name, test_name, err))
1291 self.current_test_case_info.logger.debug(
1292 "formatted exception is:\n%s" %
1293 "".join(format_exception(*err)))
1294
1295 def add_error(self, test, err, unittest_fn, error_type):
1296 if error_type == FAIL:
1297 self.log_error(test, err, 'addFailure')
1298 error_type_str = colorize("FAIL", RED)
1299 elif error_type == ERROR:
1300 self.log_error(test, err, 'addError')
1301 error_type_str = colorize("ERROR", RED)
1302 else:
1303 raise Exception('Error type %s cannot be used to record an '
1304 'error or a failure' % error_type)
1305
1306 unittest_fn(self, test, err)
1307 if self.current_test_case_info:
1308 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1309 (error_type_str,
1310 self.current_test_case_info.tempdir)
1311 self.symlink_failed()
1312 self.failed_test_cases_info.add(self.current_test_case_info)
1313 if is_core_present(self.current_test_case_info.tempdir):
1314 if not self.current_test_case_info.core_crash_test:
1315 if isinstance(test, unittest.suite._ErrorHolder):
1316 test_name = str(test)
1317 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001318 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001319 get_testcase_doc_name(test), test.id())
1320 self.current_test_case_info.core_crash_test = test_name
1321 self.core_crash_test_cases_info.add(
1322 self.current_test_case_info)
1323 else:
1324 self.result_string = '%s [no temp dir]' % error_type_str
1325
1326 self.send_result_through_pipe(test, error_type)
1327
Damjan Marionf56b77a2016-10-03 19:44:57 +02001328 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001329 """
1330 Record a test failed result
1331
1332 :param test:
1333 :param err: error message
1334
1335 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001336 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001337
Damjan Marionf56b77a2016-10-03 19:44:57 +02001338 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001339 """
1340 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001341
Klement Sekeraf62ae122016-10-11 11:47:09 +02001342 :param test:
1343 :param err: error message
1344
1345 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001346 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001347
Damjan Marionf56b77a2016-10-03 19:44:57 +02001348 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001349 """
1350 Get test description
1351
1352 :param test:
1353 :returns: test description
1354
1355 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001356 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001357
Damjan Marionf56b77a2016-10-03 19:44:57 +02001358 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001359 """
1360 Start a test
1361
1362 :param test:
1363
1364 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001365
1366 def print_header(test):
1367 if not hasattr(test.__class__, '_header_printed'):
1368 print(double_line_delim)
1369 print(colorize(getdoc(test).splitlines()[0], GREEN))
1370 print(double_line_delim)
1371 test.__class__._header_printed = True
1372
1373 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001374
Damjan Marionf56b77a2016-10-03 19:44:57 +02001375 unittest.TestResult.startTest(self, test)
1376 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001377 self.stream.writeln(
1378 "Starting " + self.getDescription(test) + " ...")
1379 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001380
Damjan Marionf56b77a2016-10-03 19:44:57 +02001381 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001382 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001383 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001384
1385 :param test:
1386
1387 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001388 unittest.TestResult.stopTest(self, test)
1389 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001390 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001391 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001392 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001393 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001394 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001395 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001396 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001397
1398 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001399
Damjan Marionf56b77a2016-10-03 19:44:57 +02001400 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001401 """
1402 Print errors from running the test case
1403 """
juraj.linkesabec0122018-11-16 17:28:56 +01001404 if len(self.errors) > 0 or len(self.failures) > 0:
1405 self.stream.writeln()
1406 self.printErrorList('ERROR', self.errors)
1407 self.printErrorList('FAIL', self.failures)
1408
1409 # ^^ that is the last output from unittest before summary
1410 if not self.runner.print_summary:
1411 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1412 self.stream = devnull
1413 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001414
Damjan Marionf56b77a2016-10-03 19:44:57 +02001415 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001416 """
1417 Print error list to the output stream together with error type
1418 and test case description.
1419
1420 :param flavour: error type
1421 :param errors: iterable errors
1422
1423 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001424 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001425 self.stream.writeln(double_line_delim)
1426 self.stream.writeln("%s: %s" %
1427 (flavour, self.getDescription(test)))
1428 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001429 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001430
1431
Damjan Marionf56b77a2016-10-03 19:44:57 +02001432class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001433 """
Klement Sekera104543f2017-02-03 07:29:43 +01001434 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001435 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001436
Klement Sekeraf62ae122016-10-11 11:47:09 +02001437 @property
1438 def resultclass(self):
1439 """Class maintaining the results of the tests"""
1440 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001441
juraj.linkes184870a2018-07-16 14:22:01 +02001442 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001443 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001444 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001445 # ignore stream setting here, use hard-coded stdout to be in sync
1446 # with prints from VppTestCase methods ...
1447 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1448 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001449 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001450 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001451
juraj.linkesabec0122018-11-16 17:28:56 +01001452 self.orig_stream = self.stream
1453 self.resultclass.test_framework_result_pipe = result_pipe
1454
1455 self.print_summary = print_summary
1456
1457 def _makeResult(self):
1458 return self.resultclass(self.stream,
1459 self.descriptions,
1460 self.verbosity,
1461 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001462
Damjan Marionf56b77a2016-10-03 19:44:57 +02001463 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001464 """
1465 Run the tests
1466
1467 :param test:
1468
1469 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001470 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001471
1472 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001473 if not self.print_summary:
1474 self.stream = self.orig_stream
1475 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001476 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001477
1478
1479class Worker(Thread):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001480 def __init__(self, args, logger, env=None):
Neale Ranns812ed392017-10-16 04:20:13 -07001481 self.logger = logger
1482 self.args = args
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001483 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001484 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001485 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001486 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001487 super(Worker, self).__init__()
1488
1489 def run(self):
1490 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001491 if not os.path.exists(executable) or not os.access(
1492 executable, os.F_OK | os.X_OK):
1493 # Exit code that means some system file did not exist,
1494 # could not be opened, or had some other kind of error.
1495 self.result = os.EX_OSFILE
1496 raise EnvironmentError(
1497 "executable '%s' is not found or executable." % executable)
Neale Ranns812ed392017-10-16 04:20:13 -07001498 self.logger.debug("Running executable w/args `%s'" % self.args)
1499 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001500 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001501 env["CK_LOG_FILE_NAME"] = "-"
1502 self.process = subprocess.Popen(
1503 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1504 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1505 out, err = self.process.communicate()
1506 self.logger.debug("Finished running `%s'" % executable)
1507 self.logger.info("Return code is `%s'" % self.process.returncode)
1508 self.logger.info(single_line_delim)
1509 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1510 self.logger.info(single_line_delim)
1511 self.logger.info(out)
1512 self.logger.info(single_line_delim)
1513 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1514 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001515 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001516 self.logger.info(single_line_delim)
1517 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001518
Klement Sekera6aa58b72019-05-16 14:34:55 +02001519
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001520if __name__ == '__main__':
1521 pass