blob: 59c451d475ca848d6eac2a2019ca61ae847b770b [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()
juraj.linkes184870a2018-07-16 14:22:01 +0200365
Ole Troana45dc072018-12-21 16:04:22 +0100366 cls.vpp_cmdline = [cls.vpp_bin, "unix",
367 "{", "nodaemon", debug_cli, "full-coredump",
368 coredump_size, "runtime-dir", cls.tempdir, "}",
369 "api-trace", "{", "on", "}", "api-segment", "{",
370 "prefix", cls.shm_prefix, "}", "cpu", "{",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200371 "main-core", str(cpu_core_number), "}",
372 "statseg", "{", "socket-name", cls.stats_sock, "}",
373 "socksvr", "{", "socket-name", cls.api_sock, "}",
374 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100375 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200376 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100377 "}", "plugin", "unittest_plugin.so", "{", "enable",
378 "}"] + cls.extra_vpp_plugin_config + ["}", ]
379 if cls.extra_vpp_punt_config is not None:
380 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100381 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100382 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400383 if cls.test_plugin_path is not None:
384 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
385
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100386 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
387 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200388
389 @classmethod
390 def wait_for_enter(cls):
391 if cls.debug_gdbserver:
392 print(double_line_delim)
393 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
394 elif cls.debug_gdb:
395 print(double_line_delim)
396 print("Spawned VPP with PID: %d" % cls.vpp.pid)
397 else:
398 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
399 return
400 print(single_line_delim)
401 print("You can debug the VPP using e.g.:")
402 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400403 print("sudo gdb " + cls.vpp_bin +
404 " -ex 'target remote localhost:7777'")
Klement Sekera277b89c2016-10-28 13:20:27 +0200405 print("Now is the time to attach a gdb by running the above "
406 "command, set up breakpoints etc. and then resume VPP from "
407 "within gdb by issuing the 'continue' command")
408 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400409 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Klement Sekera277b89c2016-10-28 13:20:27 +0200410 print("Now is the time to attach a gdb by running the above "
411 "command and set up breakpoints etc.")
412 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800413 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200414
415 @classmethod
416 def run_vpp(cls):
417 cmdline = cls.vpp_cmdline
418
419 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100420 gdbserver = '/usr/bin/gdbserver'
421 if not os.path.isfile(gdbserver) or \
422 not os.access(gdbserver, os.X_OK):
423 raise Exception("gdbserver binary '%s' does not exist or is "
424 "not executable" % gdbserver)
425
426 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200427 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
428
Klement Sekera931be3a2016-11-03 05:36:01 +0100429 try:
430 cls.vpp = subprocess.Popen(cmdline,
431 stdout=subprocess.PIPE,
432 stderr=subprocess.PIPE,
433 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800434 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800435 cls.logger.critical("Subprocess returned with non-0 return code: ("
436 "%s)", e.returncode)
437 raise
438 except OSError as e:
439 cls.logger.critical("Subprocess returned with OS error: "
440 "(%s) %s", e.errno, e.strerror)
441 raise
442 except Exception as e:
443 cls.logger.exception("Subprocess returned unexpected from "
444 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100445 raise
446
Klement Sekera277b89c2016-10-28 13:20:27 +0200447 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100448
Damjan Marionf56b77a2016-10-03 19:44:57 +0200449 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200450 def wait_for_stats_socket(cls):
451 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800452 ok = False
453 while time.time() < deadline or \
454 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200455 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800456 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200457 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700458 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800459 if not ok:
460 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200461
462 @classmethod
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400463 def wait_for_coredump(cls):
464 corefile = cls.tempdir + "/core"
465 if os.path.isfile(corefile):
466 cls.logger.error("Waiting for coredump to complete: %s", corefile)
467 curr_size = os.path.getsize(corefile)
468 deadline = time.time() + 60
469 ok = False
470 while time.time() < deadline:
471 cls.sleep(1)
472 size = curr_size
473 curr_size = os.path.getsize(corefile)
474 if size == curr_size:
475 ok = True
476 break
477 if not ok:
478 cls.logger.error("Timed out waiting for coredump to complete:"
479 " %s", corefile)
480 else:
481 cls.logger.error("Coredump complete: %s, size %d",
482 corefile, curr_size)
483
484 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200485 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200486 """
487 Perform class setup before running the testcase
488 Remove shared memory files, start vpp and connect the vpp-api
489 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800490 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100491 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100492 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100493 cls.logger = get_logger(cls.__name__)
494 if hasattr(cls, 'parallel_handler'):
495 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100496 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700497
Klement Sekeraf62ae122016-10-11 11:47:09 +0200498 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200499 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200500 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200501 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200502 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
503 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100504 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
505 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200506 cls.file_handler.setLevel(DEBUG)
507 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700508 cls.logger.debug("--- setUpClass() for %s called ---" %
509 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200510 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200511 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200512 cls.logger.info("Temporary dir is %s, shm prefix is %s",
513 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200514 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100515 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100516 cls._captures = []
517 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200518 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100519 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100520 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200521 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200522 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200523 # need to catch exceptions here because if we raise, then the cleanup
524 # doesn't get called and we might end with a zombie vpp
525 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200526 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200527 cls.reporter.send_keep_alive(cls, 'setUpClass')
528 VppTestResult.current_test_case_info = TestCaseInfo(
529 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100530 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100531 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100532 cls.pump_thread_stop_flag = Event()
533 cls.pump_thread_wakeup_pipe = os.pipe()
534 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100535 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100536 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200537 if cls.debug_gdb or cls.debug_gdbserver:
538 read_timeout = 0
539 else:
540 read_timeout = 5
541 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
542 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100543 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400544 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100545 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400546 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100547 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200548 cls.wait_for_stats_socket()
549 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200550 try:
551 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100552 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200553 cls.vpp_startup_failed = True
554 cls.logger.critical(
555 "VPP died shortly after startup, check the"
556 " output to standard error for possible cause")
557 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100558 try:
559 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100560 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100561 try:
562 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100563 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100564 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100565 if cls.debug_gdbserver:
566 print(colorize("You're running VPP inside gdbserver but "
567 "VPP-API connection failed, did you forget "
568 "to 'continue' VPP from within gdb?", RED))
569 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100570 except Exception:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400571
572 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100573 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200574
Damjan Marionf56b77a2016-10-03 19:44:57 +0200575 @classmethod
576 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200577 """
578 Disconnect vpp-api, kill vpp and cleanup shared memory files
579 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200580 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
581 cls.vpp.poll()
582 if cls.vpp.returncode is None:
583 print(double_line_delim)
584 print("VPP or GDB server is still running")
585 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800586 input("When done debugging, press ENTER to kill the "
587 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200588
juraj.linkes184870a2018-07-16 14:22:01 +0200589 # first signal that we want to stop the pump thread, then wake it up
590 if hasattr(cls, 'pump_thread_stop_flag'):
591 cls.pump_thread_stop_flag.set()
592 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100593 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100594 if hasattr(cls, 'pump_thread'):
595 cls.logger.debug("Waiting for pump thread to stop")
596 cls.pump_thread.join()
597 if hasattr(cls, 'vpp_stderr_reader_thread'):
598 cls.logger.debug("Waiting for stdderr pump to stop")
599 cls.vpp_stderr_reader_thread.join()
600
Klement Sekeraf62ae122016-10-11 11:47:09 +0200601 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100602 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700603 cls.logger.debug("Disconnecting class vapi client on %s",
604 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100605 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700606 cls.logger.debug("Deleting class vapi attribute on %s",
607 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100608 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200609 cls.vpp.poll()
610 if cls.vpp.returncode is None:
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400611 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100612 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400613 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100614 cls.logger.debug("Waiting for vpp to die")
615 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700616 cls.logger.debug("Deleting class vpp attribute on %s",
617 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200618 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200619
Klement Sekera3747c752017-04-10 06:30:17 +0200620 if cls.vpp_startup_failed:
621 stdout_log = cls.logger.info
622 stderr_log = cls.logger.critical
623 else:
624 stdout_log = cls.logger.info
625 stderr_log = cls.logger.info
626
Klement Sekerae4504c62016-12-08 10:16:41 +0100627 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200628 stdout_log(single_line_delim)
629 stdout_log('VPP output to stdout while running %s:', cls.__name__)
630 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100631 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200632 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
633 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200634 stdout_log('\n%s', vpp_output)
635 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200636
Klement Sekerae4504c62016-12-08 10:16:41 +0100637 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200638 stderr_log(single_line_delim)
639 stderr_log('VPP output to stderr while running %s:', cls.__name__)
640 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100641 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200642 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
643 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200644 stderr_log('\n%s', vpp_output)
645 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200646
Damjan Marionf56b77a2016-10-03 19:44:57 +0200647 @classmethod
648 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200649 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700650 cls.logger.debug("--- tearDownClass() for %s called ---" %
651 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200652 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200653 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200654 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100655 cls.reset_packet_infos()
656 if debug_framework:
657 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200658
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700659 def show_commands_at_teardown(self):
660 """ Allow subclass specific teardown logging additions."""
661 self.logger.info("--- No test specific show commands provided. ---")
662
Damjan Marionf56b77a2016-10-03 19:44:57 +0200663 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200664 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100665 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
666 (self.__class__.__name__, self._testMethodName,
667 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700668
669 try:
670 if not self.vpp_dead:
671 self.logger.debug(self.vapi.cli("show trace max 1000"))
672 self.logger.info(self.vapi.ppcli("show interface"))
673 self.logger.info(self.vapi.ppcli("show hardware"))
674 self.logger.info(self.statistics.set_errors_str())
675 self.logger.info(self.vapi.ppcli("show run"))
676 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400677 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700678 self.logger.info("Logging testcase specific show commands.")
679 self.show_commands_at_teardown()
680 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500681 # Save/Dump VPP api trace log
682 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
683 tmp_api_trace = "/tmp/%s" % api_trace
684 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
685 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
686 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
687 vpp_api_trace_log))
688 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500689 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500690 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700691 except VppTransportShmemIOError:
692 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
693 "Cannot log show commands.")
694 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100695 else:
696 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200697
Damjan Marionf56b77a2016-10-03 19:44:57 +0200698 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200699 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800700 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200701 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100702 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400703
704 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
705 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100706 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100707 self.vpp_stdout_deque.append(
708 "--- test setUp() for %s.%s(%s) starts here ---\n" %
709 (self.__class__.__name__, self._testMethodName,
710 self._testMethodDoc))
711 self.vpp_stderr_deque.append(
712 "--- test setUp() for %s.%s(%s) starts here ---\n" %
713 (self.__class__.__name__, self._testMethodName,
714 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200715 self.vapi.cli("clear trace")
716 # store the test instance inside the test class - so that objects
717 # holding the class can access instance methods (like assertEqual)
718 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200719
Damjan Marionf56b77a2016-10-03 19:44:57 +0200720 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200721 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200722 """
723 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200724
Klement Sekera75e7d132017-09-20 08:26:30 +0200725 :param interfaces: iterable interface indexes (if None,
726 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727
Klement Sekeraf62ae122016-10-11 11:47:09 +0200728 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200729 if interfaces is None:
730 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200731 for i in interfaces:
732 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200733
Damjan Marionf56b77a2016-10-03 19:44:57 +0200734 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100735 def register_capture(cls, cap_name):
736 """ Register a capture in the testclass """
737 # add to the list of captures with current timestamp
738 cls._captures.append((time.time(), cap_name))
739 # filter out from zombies
740 cls._zombie_captures = [(stamp, name)
741 for (stamp, name) in cls._zombie_captures
742 if name != cap_name]
743
744 @classmethod
745 def pg_start(cls):
746 """ Remove any zombie captures and enable the packet generator """
747 # how long before capture is allowed to be deleted - otherwise vpp
748 # crashes - 100ms seems enough (this shouldn't be needed at all)
749 capture_ttl = 0.1
750 now = time.time()
751 for stamp, cap_name in cls._zombie_captures:
752 wait = stamp + capture_ttl - now
753 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100754 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100755 now = time.time()
756 cls.logger.debug("Removing zombie capture %s" % cap_name)
757 cls.vapi.cli('packet-generator delete %s' % cap_name)
758
Klement Sekerad91fa612019-01-15 13:25:09 +0100759 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200760 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100761 cls._zombie_captures = cls._captures
762 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200763
Damjan Marionf56b77a2016-10-03 19:44:57 +0200764 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200765 def create_pg_interfaces(cls, interfaces):
766 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100767 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200768
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100769 :param interfaces: iterable indexes of the interfaces.
770 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200771
Klement Sekeraf62ae122016-10-11 11:47:09 +0200772 """
773 result = []
774 for i in interfaces:
775 intf = VppPGInterface(cls, i)
776 setattr(cls, intf.name, intf)
777 result.append(intf)
778 cls.pg_interfaces = result
779 return result
780
Matej Klotton0178d522016-11-04 11:11:44 +0100781 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200782 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100783 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100784 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100785
Klement Sekerab9ef2732018-06-24 22:49:33 +0200786 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100787 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100788 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200789 result = [VppLoInterface(cls) for i in range(count)]
790 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100791 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100792 cls.lo_interfaces = result
793 return result
794
Neale Ranns192b13f2019-03-15 02:16:20 -0700795 @classmethod
796 def create_bvi_interfaces(cls, count):
797 """
798 Create BVI interfaces.
799
800 :param count: number of interfaces created.
801 :returns: List of created interfaces.
802 """
803 result = [VppBviInterface(cls) for i in range(count)]
804 for intf in result:
805 setattr(cls, intf.name, intf)
806 cls.bvi_interfaces = result
807 return result
808
Damjan Marionf56b77a2016-10-03 19:44:57 +0200809 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200810 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200811 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200812 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200813 NOTE: Currently works only when Raw layer is present.
814
815 :param packet: packet
816 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200817 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200818
819 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200820 packet_len = len(packet) + 4
821 extend = size - packet_len
822 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200823 num = (extend // len(padding)) + 1
824 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200825
Klement Sekeradab231a2016-12-21 08:50:14 +0100826 @classmethod
827 def reset_packet_infos(cls):
828 """ Reset the list of packet info objects and packet counts to zero """
829 cls._packet_infos = {}
830 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200831
Klement Sekeradab231a2016-12-21 08:50:14 +0100832 @classmethod
833 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200834 """
835 Create packet info object containing the source and destination indexes
836 and add it to the testcase's packet info list
837
Klement Sekeradab231a2016-12-21 08:50:14 +0100838 :param VppInterface src_if: source interface
839 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200840
841 :returns: _PacketInfo object
842
843 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200844 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100845 info.index = len(cls._packet_infos)
846 info.src = src_if.sw_if_index
847 info.dst = dst_if.sw_if_index
848 if isinstance(dst_if, VppSubInterface):
849 dst_idx = dst_if.parent.sw_if_index
850 else:
851 dst_idx = dst_if.sw_if_index
852 if dst_idx in cls._packet_count_for_dst_if_idx:
853 cls._packet_count_for_dst_if_idx[dst_idx] += 1
854 else:
855 cls._packet_count_for_dst_if_idx[dst_idx] = 1
856 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200857 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200858
Damjan Marionf56b77a2016-10-03 19:44:57 +0200859 @staticmethod
860 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200861 """
862 Convert _PacketInfo object to packet payload
863
864 :param info: _PacketInfo object
865
866 :returns: string containing serialized data from packet info
867 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100868 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
869 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200870
Damjan Marionf56b77a2016-10-03 19:44:57 +0200871 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800872 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200873 """
874 Convert packet payload to _PacketInfo object
875
876 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700877 :type payload: <class 'scapy.packet.Raw'>
878 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800879 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700880 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200881 :returns: _PacketInfo object containing de-serialized data from payload
882
883 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800884 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200885 info = _PacketInfo()
886 info.index = int(numbers[0])
887 info.src = int(numbers[1])
888 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100889 info.ip = int(numbers[3])
890 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200891 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200892
Damjan Marionf56b77a2016-10-03 19:44:57 +0200893 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200894 """
895 Iterate over the packet info list stored in the testcase
896 Start iteration with first element if info is None
897 Continue based on index in info if info is specified
898
899 :param info: info or None
900 :returns: next info in list or None if no more infos
901 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200902 if info is None:
903 next_index = 0
904 else:
905 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100906 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200907 return None
908 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100909 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200910
Klement Sekeraf62ae122016-10-11 11:47:09 +0200911 def get_next_packet_info_for_interface(self, src_index, info):
912 """
913 Search the packet info list for the next packet info with same source
914 interface index
915
916 :param src_index: source interface index to search for
917 :param info: packet info - where to start the search
918 :returns: packet info or None
919
920 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200921 while True:
922 info = self.get_next_packet_info(info)
923 if info is None:
924 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200925 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200926 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200927
Klement Sekeraf62ae122016-10-11 11:47:09 +0200928 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
929 """
930 Search the packet info list for the next packet info with same source
931 and destination interface indexes
932
933 :param src_index: source interface index to search for
934 :param dst_index: destination interface index to search for
935 :param info: packet info - where to start the search
936 :returns: packet info or None
937
938 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200939 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200940 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200941 if info is None:
942 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200943 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200944 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200945
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200946 def assert_equal(self, real_value, expected_value, name_or_class=None):
947 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100948 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200949 return
950 try:
951 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
952 msg = msg % (getdoc(name_or_class).strip(),
953 real_value, str(name_or_class(real_value)),
954 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100955 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200956 msg = "Invalid %s: %s does not match expected value %s" % (
957 name_or_class, real_value, expected_value)
958
959 self.assertEqual(real_value, expected_value, msg)
960
Klement Sekerab17dd962017-01-09 07:43:48 +0100961 def assert_in_range(self,
962 real_value,
963 expected_min,
964 expected_max,
965 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200966 if name is None:
967 msg = None
968 else:
969 msg = "Invalid %s: %s out of range <%s,%s>" % (
970 name, real_value, expected_min, expected_max)
971 self.assertTrue(expected_min <= real_value <= expected_max, msg)
972
Klement Sekerad81ae412018-05-16 10:52:54 +0200973 def assert_packet_checksums_valid(self, packet,
974 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700975 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200976 self.logger.debug(
977 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200978 udp_layers = ['UDP', 'UDPerror']
979 checksum_fields = ['cksum', 'chksum']
980 checksums = []
981 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700982 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200983 while True:
984 layer = temp.getlayer(counter)
985 if layer:
986 for cf in checksum_fields:
987 if hasattr(layer, cf):
988 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +0200989 0 == getattr(layer, cf) and \
990 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200991 continue
992 delattr(layer, cf)
993 checksums.append((counter, cf))
994 else:
995 break
996 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200997 if 0 == len(checksums):
998 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700999 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001000 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001001 calc_sum = getattr(temp[layer], cf)
1002 self.assert_equal(
1003 getattr(received[layer], cf), calc_sum,
1004 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1005 self.logger.debug(
1006 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1007 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001008
1009 def assert_checksum_valid(self, received_packet, layer,
1010 field_name='chksum',
1011 ignore_zero_checksum=False):
1012 """ Check checksum of received packet on given layer """
1013 received_packet_checksum = getattr(received_packet[layer], field_name)
1014 if ignore_zero_checksum and 0 == received_packet_checksum:
1015 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001016 recalculated = received_packet.__class__(
1017 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001018 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001019 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001020 self.assert_equal(received_packet_checksum,
1021 getattr(recalculated[layer], field_name),
1022 "packet checksum on layer: %s" % layer)
1023
1024 def assert_ip_checksum_valid(self, received_packet,
1025 ignore_zero_checksum=False):
1026 self.assert_checksum_valid(received_packet, 'IP',
1027 ignore_zero_checksum=ignore_zero_checksum)
1028
1029 def assert_tcp_checksum_valid(self, received_packet,
1030 ignore_zero_checksum=False):
1031 self.assert_checksum_valid(received_packet, 'TCP',
1032 ignore_zero_checksum=ignore_zero_checksum)
1033
1034 def assert_udp_checksum_valid(self, received_packet,
1035 ignore_zero_checksum=True):
1036 self.assert_checksum_valid(received_packet, 'UDP',
1037 ignore_zero_checksum=ignore_zero_checksum)
1038
1039 def assert_embedded_icmp_checksum_valid(self, received_packet):
1040 if received_packet.haslayer(IPerror):
1041 self.assert_checksum_valid(received_packet, 'IPerror')
1042 if received_packet.haslayer(TCPerror):
1043 self.assert_checksum_valid(received_packet, 'TCPerror')
1044 if received_packet.haslayer(UDPerror):
1045 self.assert_checksum_valid(received_packet, 'UDPerror',
1046 ignore_zero_checksum=True)
1047 if received_packet.haslayer(ICMPerror):
1048 self.assert_checksum_valid(received_packet, 'ICMPerror')
1049
1050 def assert_icmp_checksum_valid(self, received_packet):
1051 self.assert_checksum_valid(received_packet, 'ICMP')
1052 self.assert_embedded_icmp_checksum_valid(received_packet)
1053
1054 def assert_icmpv6_checksum_valid(self, pkt):
1055 if pkt.haslayer(ICMPv6DestUnreach):
1056 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1057 self.assert_embedded_icmp_checksum_valid(pkt)
1058 if pkt.haslayer(ICMPv6EchoRequest):
1059 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1060 if pkt.haslayer(ICMPv6EchoReply):
1061 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1062
Klement Sekera3a343d42019-05-16 14:35:46 +02001063 def get_packet_counter(self, counter):
1064 if counter.startswith("/"):
1065 counter_value = self.statistics.get_counter(counter)
1066 else:
1067 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001068 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001069 for i in range(1, len(counters) - 1):
1070 results = counters[i].split()
1071 if results[1] == counter:
1072 counter_value = int(results[0])
1073 break
1074 return counter_value
1075
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001076 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001077 counter_value = self.get_packet_counter(counter)
1078 self.assert_equal(counter_value, expected_value,
1079 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001080
Ole Troan233e4682019-05-16 15:01:34 +02001081 def assert_error_counter_equal(self, counter, expected_value):
1082 counter_value = self.statistics.get_err_counter(counter)
1083 self.assert_equal(counter_value, expected_value,
1084 "error counter `%s'" % counter)
1085
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001086 @classmethod
1087 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001088
1089 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1090 # * by Guido, only the main thread can be interrupted.
1091 # */
1092 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1093 if timeout == 0:
1094 # yield quantum
1095 if hasattr(os, 'sched_yield'):
1096 os.sched_yield()
1097 else:
1098 time.sleep(0)
1099 return
1100
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001101 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001102 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001103 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001104 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001105 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001106 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001107 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001108 "slept for %es instead of ~%es!",
1109 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001110 if hasattr(cls, 'logger'):
1111 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001112 "Finished sleep (%s) - slept %es (wanted %es)",
1113 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001114
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001115 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001116 self.vapi.cli("clear trace")
1117 intf.add_stream(pkts)
1118 self.pg_enable_capture(self.pg_interfaces)
1119 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001120
1121 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1122 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001123 if not timeout:
1124 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001125 for i in self.pg_interfaces:
1126 i.get_capture(0, timeout=timeout)
1127 i.assert_nothing_captured(remark=remark)
1128 timeout = 0.1
1129
Neale Rannsd7603d92019-03-28 08:56:10 +00001130 def send_and_expect(self, intf, pkts, output, n_rx=None):
1131 if not n_rx:
1132 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001133 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001134 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001135 return rx
1136
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001137 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1138 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001139 rx = output.get_capture(len(pkts))
1140 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001141 if not timeout:
1142 timeout = 1
1143 for i in self.pg_interfaces:
1144 if i not in outputs:
1145 i.get_capture(0, timeout=timeout)
1146 i.assert_nothing_captured()
1147 timeout = 0.1
1148
Neale Ranns52fae862018-01-08 04:41:42 -08001149 return rx
1150
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001151 def runTest(self):
1152 """ unittest calls runTest when TestCase is instantiated without a
1153 test case. Use case: Writing unittests against VppTestCase"""
1154 pass
1155
Damjan Marionf56b77a2016-10-03 19:44:57 +02001156
juraj.linkes184870a2018-07-16 14:22:01 +02001157def get_testcase_doc_name(test):
1158 return getdoc(test.__class__).splitlines()[0]
1159
1160
Ole Trøan5ba91592018-11-22 10:01:09 +00001161def get_test_description(descriptions, test):
1162 short_description = test.shortDescription()
1163 if descriptions and short_description:
1164 return short_description
1165 else:
1166 return str(test)
1167
1168
juraj.linkes40dd73b2018-09-21 13:55:16 +02001169class TestCaseInfo(object):
1170 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1171 self.logger = logger
1172 self.tempdir = tempdir
1173 self.vpp_pid = vpp_pid
1174 self.vpp_bin_path = vpp_bin_path
1175 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001176
1177
Damjan Marionf56b77a2016-10-03 19:44:57 +02001178class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001179 """
1180 @property result_string
1181 String variable to store the test case result string.
1182 @property errors
1183 List variable containing 2-tuples of TestCase instances and strings
1184 holding formatted tracebacks. Each tuple represents a test which
1185 raised an unexpected exception.
1186 @property failures
1187 List variable containing 2-tuples of TestCase instances and strings
1188 holding formatted tracebacks. Each tuple represents a test where
1189 a failure was explicitly signalled using the TestCase.assert*()
1190 methods.
1191 """
1192
juraj.linkes40dd73b2018-09-21 13:55:16 +02001193 failed_test_cases_info = set()
1194 core_crash_test_cases_info = set()
1195 current_test_case_info = None
1196
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001197 def __init__(self, stream=None, descriptions=None, verbosity=None,
1198 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001199 """
Klement Sekerada505f62017-01-04 12:58:53 +01001200 :param stream File descriptor to store where to report test results.
1201 Set to the standard error stream by default.
1202 :param descriptions Boolean variable to store information if to use
1203 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001204 :param verbosity Integer variable to store required verbosity level.
1205 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001206 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001207 self.stream = stream
1208 self.descriptions = descriptions
1209 self.verbosity = verbosity
1210 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001211 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001212
Damjan Marionf56b77a2016-10-03 19:44:57 +02001213 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001214 """
1215 Record a test succeeded result
1216
1217 :param test:
1218
1219 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001220 if self.current_test_case_info:
1221 self.current_test_case_info.logger.debug(
1222 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1223 test._testMethodName,
1224 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001225 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001226 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001227
juraj.linkescae64f82018-09-19 15:01:47 +02001228 self.send_result_through_pipe(test, PASS)
1229
Klement Sekeraf62ae122016-10-11 11:47:09 +02001230 def addSkip(self, test, reason):
1231 """
1232 Record a test skipped.
1233
1234 :param test:
1235 :param reason:
1236
1237 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001238 if self.current_test_case_info:
1239 self.current_test_case_info.logger.debug(
1240 "--- addSkip() %s.%s(%s) called, reason is %s" %
1241 (test.__class__.__name__, test._testMethodName,
1242 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001243 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001244 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001245
juraj.linkescae64f82018-09-19 15:01:47 +02001246 self.send_result_through_pipe(test, SKIP)
1247
juraj.linkes40dd73b2018-09-21 13:55:16 +02001248 def symlink_failed(self):
1249 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001250 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001251 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001252 link_path = os.path.join(
1253 failed_dir,
1254 '%s-FAILED' %
1255 os.path.basename(self.current_test_case_info.tempdir))
1256 if self.current_test_case_info.logger:
1257 self.current_test_case_info.logger.debug(
1258 "creating a link to the failed test")
1259 self.current_test_case_info.logger.debug(
1260 "os.symlink(%s, %s)" %
1261 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001262 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001263 if self.current_test_case_info.logger:
1264 self.current_test_case_info.logger.debug(
1265 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001266 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001267 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001268
Klement Sekeraf413bef2017-08-15 07:09:02 +02001269 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001270 if self.current_test_case_info.logger:
1271 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001272
juraj.linkescae64f82018-09-19 15:01:47 +02001273 def send_result_through_pipe(self, test, result):
1274 if hasattr(self, 'test_framework_result_pipe'):
1275 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001276 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001277 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001278
juraj.linkes40dd73b2018-09-21 13:55:16 +02001279 def log_error(self, test, err, fn_name):
1280 if self.current_test_case_info:
1281 if isinstance(test, unittest.suite._ErrorHolder):
1282 test_name = test.description
1283 else:
1284 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1285 test._testMethodName,
1286 test._testMethodDoc)
1287 self.current_test_case_info.logger.debug(
1288 "--- %s() %s called, err is %s" %
1289 (fn_name, test_name, err))
1290 self.current_test_case_info.logger.debug(
1291 "formatted exception is:\n%s" %
1292 "".join(format_exception(*err)))
1293
1294 def add_error(self, test, err, unittest_fn, error_type):
1295 if error_type == FAIL:
1296 self.log_error(test, err, 'addFailure')
1297 error_type_str = colorize("FAIL", RED)
1298 elif error_type == ERROR:
1299 self.log_error(test, err, 'addError')
1300 error_type_str = colorize("ERROR", RED)
1301 else:
1302 raise Exception('Error type %s cannot be used to record an '
1303 'error or a failure' % error_type)
1304
1305 unittest_fn(self, test, err)
1306 if self.current_test_case_info:
1307 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1308 (error_type_str,
1309 self.current_test_case_info.tempdir)
1310 self.symlink_failed()
1311 self.failed_test_cases_info.add(self.current_test_case_info)
1312 if is_core_present(self.current_test_case_info.tempdir):
1313 if not self.current_test_case_info.core_crash_test:
1314 if isinstance(test, unittest.suite._ErrorHolder):
1315 test_name = str(test)
1316 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001317 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001318 get_testcase_doc_name(test), test.id())
1319 self.current_test_case_info.core_crash_test = test_name
1320 self.core_crash_test_cases_info.add(
1321 self.current_test_case_info)
1322 else:
1323 self.result_string = '%s [no temp dir]' % error_type_str
1324
1325 self.send_result_through_pipe(test, error_type)
1326
Damjan Marionf56b77a2016-10-03 19:44:57 +02001327 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001328 """
1329 Record a test failed result
1330
1331 :param test:
1332 :param err: error message
1333
1334 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001335 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001336
Damjan Marionf56b77a2016-10-03 19:44:57 +02001337 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001338 """
1339 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001340
Klement Sekeraf62ae122016-10-11 11:47:09 +02001341 :param test:
1342 :param err: error message
1343
1344 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001345 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001346
Damjan Marionf56b77a2016-10-03 19:44:57 +02001347 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001348 """
1349 Get test description
1350
1351 :param test:
1352 :returns: test description
1353
1354 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001355 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001356
Damjan Marionf56b77a2016-10-03 19:44:57 +02001357 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001358 """
1359 Start a test
1360
1361 :param test:
1362
1363 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001364
1365 def print_header(test):
1366 if not hasattr(test.__class__, '_header_printed'):
1367 print(double_line_delim)
1368 print(colorize(getdoc(test).splitlines()[0], GREEN))
1369 print(double_line_delim)
1370 test.__class__._header_printed = True
1371
1372 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001373
Damjan Marionf56b77a2016-10-03 19:44:57 +02001374 unittest.TestResult.startTest(self, test)
1375 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001376 self.stream.writeln(
1377 "Starting " + self.getDescription(test) + " ...")
1378 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001379
Damjan Marionf56b77a2016-10-03 19:44:57 +02001380 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001381 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001382 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001383
1384 :param test:
1385
1386 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001387 unittest.TestResult.stopTest(self, test)
1388 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001389 self.stream.writeln(single_line_delim)
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))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001392 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001393 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001394 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001395 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001396
1397 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001398
Damjan Marionf56b77a2016-10-03 19:44:57 +02001399 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001400 """
1401 Print errors from running the test case
1402 """
juraj.linkesabec0122018-11-16 17:28:56 +01001403 if len(self.errors) > 0 or len(self.failures) > 0:
1404 self.stream.writeln()
1405 self.printErrorList('ERROR', self.errors)
1406 self.printErrorList('FAIL', self.failures)
1407
1408 # ^^ that is the last output from unittest before summary
1409 if not self.runner.print_summary:
1410 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1411 self.stream = devnull
1412 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001413
Damjan Marionf56b77a2016-10-03 19:44:57 +02001414 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001415 """
1416 Print error list to the output stream together with error type
1417 and test case description.
1418
1419 :param flavour: error type
1420 :param errors: iterable errors
1421
1422 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001423 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001424 self.stream.writeln(double_line_delim)
1425 self.stream.writeln("%s: %s" %
1426 (flavour, self.getDescription(test)))
1427 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001428 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001429
1430
Damjan Marionf56b77a2016-10-03 19:44:57 +02001431class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001432 """
Klement Sekera104543f2017-02-03 07:29:43 +01001433 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001434 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001435
Klement Sekeraf62ae122016-10-11 11:47:09 +02001436 @property
1437 def resultclass(self):
1438 """Class maintaining the results of the tests"""
1439 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001440
juraj.linkes184870a2018-07-16 14:22:01 +02001441 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001442 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001443 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001444 # ignore stream setting here, use hard-coded stdout to be in sync
1445 # with prints from VppTestCase methods ...
1446 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1447 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001448 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001449 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001450
juraj.linkesabec0122018-11-16 17:28:56 +01001451 self.orig_stream = self.stream
1452 self.resultclass.test_framework_result_pipe = result_pipe
1453
1454 self.print_summary = print_summary
1455
1456 def _makeResult(self):
1457 return self.resultclass(self.stream,
1458 self.descriptions,
1459 self.verbosity,
1460 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001461
Damjan Marionf56b77a2016-10-03 19:44:57 +02001462 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001463 """
1464 Run the tests
1465
1466 :param test:
1467
1468 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001469 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001470
1471 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001472 if not self.print_summary:
1473 self.stream = self.orig_stream
1474 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001475 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001476
1477
1478class Worker(Thread):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001479 def __init__(self, args, logger, env=None):
Neale Ranns812ed392017-10-16 04:20:13 -07001480 self.logger = logger
1481 self.args = args
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001482 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001483 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001484 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001485 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001486 super(Worker, self).__init__()
1487
1488 def run(self):
1489 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001490 if not os.path.exists(executable) or not os.access(
1491 executable, os.F_OK | os.X_OK):
1492 # Exit code that means some system file did not exist,
1493 # could not be opened, or had some other kind of error.
1494 self.result = os.EX_OSFILE
1495 raise EnvironmentError(
1496 "executable '%s' is not found or executable." % executable)
Neale Ranns812ed392017-10-16 04:20:13 -07001497 self.logger.debug("Running executable w/args `%s'" % self.args)
1498 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001499 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001500 env["CK_LOG_FILE_NAME"] = "-"
1501 self.process = subprocess.Popen(
1502 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1503 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1504 out, err = self.process.communicate()
1505 self.logger.debug("Finished running `%s'" % executable)
1506 self.logger.info("Return code is `%s'" % self.process.returncode)
1507 self.logger.info(single_line_delim)
1508 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1509 self.logger.info(single_line_delim)
1510 self.logger.info(out)
1511 self.logger.info(single_line_delim)
1512 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1513 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001514 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001515 self.logger.info(single_line_delim)
1516 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001517
Klement Sekera6aa58b72019-05-16 14:34:55 +02001518
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001519if __name__ == '__main__':
1520 pass