blob: 1be227a3ead4148b42385f1019ec1ce6157d13e3 [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:
Ole Troan45ec5702019-10-17 01:53:47 +0200162 split = read.decode('ascii',
163 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100164 if len(stdout_fragment) > 0:
165 split[0] = "%s%s" % (stdout_fragment, split[0])
166 if len(split) > 0 and split[-1].endswith("\n"):
167 limit = None
168 else:
169 limit = -1
170 stdout_fragment = split[-1]
171 testclass.vpp_stdout_deque.extend(split[:limit])
172 if not testclass.cache_vpp_output:
173 for line in split[:limit]:
174 testclass.logger.debug(
175 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100176 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100177 read = os.read(testclass.vpp.stderr.fileno(), 102400)
178 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200179 split = read.decode('ascii',
180 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100181 if len(stderr_fragment) > 0:
182 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200183 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100184 limit = None
185 else:
186 limit = -1
187 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200188
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100189 testclass.vpp_stderr_deque.extend(split[:limit])
190 if not testclass.cache_vpp_output:
191 for line in split[:limit]:
192 testclass.logger.debug(
193 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800194 # ignoring the dummy pipe here intentionally - the
195 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200196
197
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800198def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400199 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100200
Klement Sekera6aa58b72019-05-16 14:34:55 +0200201
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800202is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100203
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800204
205def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100206 return platform.machine() == 'aarch64'
207
Klement Sekera6aa58b72019-05-16 14:34:55 +0200208
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800209is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100210
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800211
212def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400213 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100214
Klement Sekera6aa58b72019-05-16 14:34:55 +0200215
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800216running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100217
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800218
219def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100220 os_id = os.getenv("OS_ID", "")
221 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200222
Klement Sekera6aa58b72019-05-16 14:34:55 +0200223
Klement Sekera3a350702019-09-02 14:26:26 +0000224running_on_centos = _running_on_centos()
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800225
Klement Sekerad3e671e2017-09-29 12:36:37 +0200226
Klement Sekera909a6a12017-08-08 04:33:53 +0200227class KeepAliveReporter(object):
228 """
229 Singleton object which reports test start to parent process
230 """
231 _shared_state = {}
232
233 def __init__(self):
234 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800235 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200236
237 @property
238 def pipe(self):
239 return self._pipe
240
241 @pipe.setter
242 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800243 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200244 raise Exception("Internal error - pipe should only be set once.")
245 self._pipe = pipe
246
juraj.linkes40dd73b2018-09-21 13:55:16 +0200247 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200248 """
249 Write current test tmpdir & desc to keep-alive pipe to signal liveness
250 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200251 if self.pipe is None:
252 # if not running forked..
253 return
254
Klement Sekera909a6a12017-08-08 04:33:53 +0200255 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200256 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200257 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200258 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200259
Dave Wallacee2efd122017-09-30 22:04:21 -0400260 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200261
262
Damjan Marionf56b77a2016-10-03 19:44:57 +0200263class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100264 """This subclass is a base class for VPP test cases that are implemented as
265 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200266 """
267
Ole Troana45dc072018-12-21 16:04:22 +0100268 extra_vpp_punt_config = []
269 extra_vpp_plugin_config = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100270
Klement Sekeraf62ae122016-10-11 11:47:09 +0200271 @property
272 def packet_infos(self):
273 """List of packet infos"""
274 return self._packet_infos
275
Klement Sekeradab231a2016-12-21 08:50:14 +0100276 @classmethod
277 def get_packet_count_for_if_idx(cls, dst_if_index):
278 """Get the number of packet info for specified destination if index"""
279 if dst_if_index in cls._packet_count_for_dst_if_idx:
280 return cls._packet_count_for_dst_if_idx[dst_if_index]
281 else:
282 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200283
284 @classmethod
285 def instance(cls):
286 """Return the instance of this testcase"""
287 return cls.test_instance
288
Damjan Marionf56b77a2016-10-03 19:44:57 +0200289 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200290 def set_debug_flags(cls, d):
291 cls.debug_core = False
292 cls.debug_gdb = False
293 cls.debug_gdbserver = False
294 if d is None:
295 return
296 dl = d.lower()
297 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200298 cls.debug_core = True
299 elif dl == "gdb":
300 cls.debug_gdb = True
301 elif dl == "gdbserver":
302 cls.debug_gdbserver = True
303 else:
304 raise Exception("Unrecognized DEBUG option: '%s'" % d)
305
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800306 @staticmethod
307 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200308 cpu_usage_list = [set(range(psutil.cpu_count()))]
309 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
310 if 'vpp_main' == p.info['name']]
311 for vpp_process in vpp_processes:
312 for cpu_usage_set in cpu_usage_list:
313 try:
314 cpu_num = vpp_process.cpu_num()
315 if cpu_num in cpu_usage_set:
316 cpu_usage_set_index = cpu_usage_list.index(
317 cpu_usage_set)
318 if cpu_usage_set_index == len(cpu_usage_list) - 1:
319 cpu_usage_list.append({cpu_num})
320 else:
321 cpu_usage_list[cpu_usage_set_index + 1].add(
322 cpu_num)
323 cpu_usage_set.remove(cpu_num)
324 break
325 except psutil.NoSuchProcess:
326 pass
327
328 for cpu_usage_set in cpu_usage_list:
329 if len(cpu_usage_set) > 0:
330 min_usage_set = cpu_usage_set
331 break
332
333 return random.choice(tuple(min_usage_set))
334
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800335 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200336 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200337 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400338 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100339 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400340 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100341 c = os.getenv("CACHE_OUTPUT", "1")
342 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200343 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100344 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
345 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400346 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100347 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
348 plugin_path = None
349 if cls.plugin_path is not None:
350 if cls.extern_plugin_path is not None:
351 plugin_path = "%s:%s" % (
352 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100353 else:
354 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100355 elif cls.extern_plugin_path is not None:
356 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100357 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100358 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100359 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100360 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100361 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100362 if size is not None:
363 coredump_size = "coredump-size %s" % size
364 if coredump_size is None:
365 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200366
Ole Troana45dc072018-12-21 16:04:22 +0100367 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000368 if not hasattr(cls, "worker_config"):
369 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200370
Ole Troana45dc072018-12-21 16:04:22 +0100371 cls.vpp_cmdline = [cls.vpp_bin, "unix",
372 "{", "nodaemon", debug_cli, "full-coredump",
373 coredump_size, "runtime-dir", cls.tempdir, "}",
374 "api-trace", "{", "on", "}", "api-segment", "{",
375 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000376 "main-core", str(cpu_core_number),
377 cls.worker_config, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200378 "statseg", "{", "socket-name", cls.stats_sock, "}",
379 "socksvr", "{", "socket-name", cls.api_sock, "}",
380 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100381 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200382 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100383 "}", "plugin", "unittest_plugin.so", "{", "enable",
384 "}"] + cls.extra_vpp_plugin_config + ["}", ]
385 if cls.extra_vpp_punt_config is not None:
386 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100387 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100388 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400389 if cls.test_plugin_path is not None:
390 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
391
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100392 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
393 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200394
395 @classmethod
396 def wait_for_enter(cls):
397 if cls.debug_gdbserver:
398 print(double_line_delim)
399 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
400 elif cls.debug_gdb:
401 print(double_line_delim)
402 print("Spawned VPP with PID: %d" % cls.vpp.pid)
403 else:
404 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
405 return
406 print(single_line_delim)
407 print("You can debug the VPP using e.g.:")
408 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400409 print("sudo gdb " + cls.vpp_bin +
410 " -ex 'target remote localhost:7777'")
Klement Sekera277b89c2016-10-28 13:20:27 +0200411 print("Now is the time to attach a gdb by running the above "
412 "command, set up breakpoints etc. and then resume VPP from "
413 "within gdb by issuing the 'continue' command")
414 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400415 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Klement Sekera277b89c2016-10-28 13:20:27 +0200416 print("Now is the time to attach a gdb by running the above "
417 "command and set up breakpoints etc.")
418 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800419 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200420
421 @classmethod
422 def run_vpp(cls):
423 cmdline = cls.vpp_cmdline
424
425 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100426 gdbserver = '/usr/bin/gdbserver'
427 if not os.path.isfile(gdbserver) or \
428 not os.access(gdbserver, os.X_OK):
429 raise Exception("gdbserver binary '%s' does not exist or is "
430 "not executable" % gdbserver)
431
432 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200433 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
434
Klement Sekera931be3a2016-11-03 05:36:01 +0100435 try:
436 cls.vpp = subprocess.Popen(cmdline,
437 stdout=subprocess.PIPE,
438 stderr=subprocess.PIPE,
439 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800440 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800441 cls.logger.critical("Subprocess returned with non-0 return code: ("
442 "%s)", e.returncode)
443 raise
444 except OSError as e:
445 cls.logger.critical("Subprocess returned with OS error: "
446 "(%s) %s", e.errno, e.strerror)
447 raise
448 except Exception as e:
449 cls.logger.exception("Subprocess returned unexpected from "
450 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100451 raise
452
Klement Sekera277b89c2016-10-28 13:20:27 +0200453 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100454
Damjan Marionf56b77a2016-10-03 19:44:57 +0200455 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200456 def wait_for_stats_socket(cls):
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000457 deadline = time.time() + 300
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800458 ok = False
459 while time.time() < deadline or \
460 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200461 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800462 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200463 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700464 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800465 if not ok:
466 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200467
468 @classmethod
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400469 def wait_for_coredump(cls):
470 corefile = cls.tempdir + "/core"
471 if os.path.isfile(corefile):
472 cls.logger.error("Waiting for coredump to complete: %s", corefile)
473 curr_size = os.path.getsize(corefile)
474 deadline = time.time() + 60
475 ok = False
476 while time.time() < deadline:
477 cls.sleep(1)
478 size = curr_size
479 curr_size = os.path.getsize(corefile)
480 if size == curr_size:
481 ok = True
482 break
483 if not ok:
484 cls.logger.error("Timed out waiting for coredump to complete:"
485 " %s", corefile)
486 else:
487 cls.logger.error("Coredump complete: %s, size %d",
488 corefile, curr_size)
489
490 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200491 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200492 """
493 Perform class setup before running the testcase
494 Remove shared memory files, start vpp and connect the vpp-api
495 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800496 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100497 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100498 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100499 cls.logger = get_logger(cls.__name__)
500 if hasattr(cls, 'parallel_handler'):
501 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100502 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700503
Klement Sekeraf62ae122016-10-11 11:47:09 +0200504 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200505 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200506 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200507 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200508 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
509 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100510 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
511 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200512 cls.file_handler.setLevel(DEBUG)
513 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700514 cls.logger.debug("--- setUpClass() for %s called ---" %
515 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200516 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200517 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200518 cls.logger.info("Temporary dir is %s, shm prefix is %s",
519 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200520 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100521 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100522 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200523 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100524 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100525 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200526 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200527 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200528 # need to catch exceptions here because if we raise, then the cleanup
529 # doesn't get called and we might end with a zombie vpp
530 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200531 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200532 cls.reporter.send_keep_alive(cls, 'setUpClass')
533 VppTestResult.current_test_case_info = TestCaseInfo(
534 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100535 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100536 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100537 cls.pump_thread_stop_flag = Event()
538 cls.pump_thread_wakeup_pipe = os.pipe()
539 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100540 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100541 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200542 if cls.debug_gdb or cls.debug_gdbserver:
543 read_timeout = 0
544 else:
545 read_timeout = 5
546 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
547 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100548 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400549 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100550 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400551 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100552 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200553 cls.wait_for_stats_socket()
554 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200555 try:
556 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100557 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200558 cls.vpp_startup_failed = True
559 cls.logger.critical(
560 "VPP died shortly after startup, check the"
561 " output to standard error for possible cause")
562 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100563 try:
564 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100565 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100566 try:
567 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100568 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100569 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100570 if cls.debug_gdbserver:
571 print(colorize("You're running VPP inside gdbserver but "
572 "VPP-API connection failed, did you forget "
573 "to 'continue' VPP from within gdb?", RED))
574 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000575 except Exception as e:
576 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400577
578 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100579 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200580
Damjan Marionf56b77a2016-10-03 19:44:57 +0200581 @classmethod
582 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200583 """
584 Disconnect vpp-api, kill vpp and cleanup shared memory files
585 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200586 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
587 cls.vpp.poll()
588 if cls.vpp.returncode is None:
589 print(double_line_delim)
590 print("VPP or GDB server is still running")
591 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800592 input("When done debugging, press ENTER to kill the "
593 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200594
juraj.linkes184870a2018-07-16 14:22:01 +0200595 # first signal that we want to stop the pump thread, then wake it up
596 if hasattr(cls, 'pump_thread_stop_flag'):
597 cls.pump_thread_stop_flag.set()
598 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100599 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100600 if hasattr(cls, 'pump_thread'):
601 cls.logger.debug("Waiting for pump thread to stop")
602 cls.pump_thread.join()
603 if hasattr(cls, 'vpp_stderr_reader_thread'):
604 cls.logger.debug("Waiting for stdderr pump to stop")
605 cls.vpp_stderr_reader_thread.join()
606
Klement Sekeraf62ae122016-10-11 11:47:09 +0200607 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100608 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700609 cls.logger.debug("Disconnecting class vapi client on %s",
610 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100611 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700612 cls.logger.debug("Deleting class vapi attribute on %s",
613 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100614 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200615 cls.vpp.poll()
616 if cls.vpp.returncode is None:
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400617 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100618 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400619 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100620 cls.logger.debug("Waiting for vpp to die")
621 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700622 cls.logger.debug("Deleting class vpp attribute on %s",
623 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200624 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200625
Klement Sekera3747c752017-04-10 06:30:17 +0200626 if cls.vpp_startup_failed:
627 stdout_log = cls.logger.info
628 stderr_log = cls.logger.critical
629 else:
630 stdout_log = cls.logger.info
631 stderr_log = cls.logger.info
632
Klement Sekerae4504c62016-12-08 10:16:41 +0100633 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200634 stdout_log(single_line_delim)
635 stdout_log('VPP output to stdout while running %s:', cls.__name__)
636 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100637 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200638 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
639 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200640 stdout_log('\n%s', vpp_output)
641 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200642
Klement Sekerae4504c62016-12-08 10:16:41 +0100643 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200644 stderr_log(single_line_delim)
645 stderr_log('VPP output to stderr while running %s:', cls.__name__)
646 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100647 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200648 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
649 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200650 stderr_log('\n%s', vpp_output)
651 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200652
Damjan Marionf56b77a2016-10-03 19:44:57 +0200653 @classmethod
654 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200655 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700656 cls.logger.debug("--- tearDownClass() for %s called ---" %
657 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200658 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200659 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200660 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100661 cls.reset_packet_infos()
662 if debug_framework:
663 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200664
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700665 def show_commands_at_teardown(self):
666 """ Allow subclass specific teardown logging additions."""
667 self.logger.info("--- No test specific show commands provided. ---")
668
Damjan Marionf56b77a2016-10-03 19:44:57 +0200669 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200670 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100671 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
672 (self.__class__.__name__, self._testMethodName,
673 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700674
675 try:
676 if not self.vpp_dead:
677 self.logger.debug(self.vapi.cli("show trace max 1000"))
678 self.logger.info(self.vapi.ppcli("show interface"))
679 self.logger.info(self.vapi.ppcli("show hardware"))
680 self.logger.info(self.statistics.set_errors_str())
681 self.logger.info(self.vapi.ppcli("show run"))
682 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400683 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700684 self.logger.info("Logging testcase specific show commands.")
685 self.show_commands_at_teardown()
686 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500687 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000688 m = self._testMethodName
689 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500690 tmp_api_trace = "/tmp/%s" % api_trace
691 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
692 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
693 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
694 vpp_api_trace_log))
695 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500696 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500697 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700698 except VppTransportShmemIOError:
699 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
700 "Cannot log show commands.")
701 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100702 else:
703 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200704
Damjan Marionf56b77a2016-10-03 19:44:57 +0200705 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200706 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800707 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200708 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100709 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400710
711 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
712 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100713 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100714 self.vpp_stdout_deque.append(
715 "--- test setUp() for %s.%s(%s) starts here ---\n" %
716 (self.__class__.__name__, self._testMethodName,
717 self._testMethodDoc))
718 self.vpp_stderr_deque.append(
719 "--- test setUp() for %s.%s(%s) starts here ---\n" %
720 (self.__class__.__name__, self._testMethodName,
721 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200722 self.vapi.cli("clear trace")
723 # store the test instance inside the test class - so that objects
724 # holding the class can access instance methods (like assertEqual)
725 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200726
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200728 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200729 """
730 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200731
Klement Sekera75e7d132017-09-20 08:26:30 +0200732 :param interfaces: iterable interface indexes (if None,
733 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200734
Klement Sekeraf62ae122016-10-11 11:47:09 +0200735 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200736 if interfaces is None:
737 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200738 for i in interfaces:
739 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200740
Damjan Marionf56b77a2016-10-03 19:44:57 +0200741 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100742 def register_capture(cls, cap_name):
743 """ Register a capture in the testclass """
744 # add to the list of captures with current timestamp
745 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100746
747 @classmethod
748 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000749 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100750 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200751 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000752 # PG, when starts, runs to completion -
753 # so let's avoid a race condition,
754 # and wait a little till it's done.
755 # Then clean it up - and then be gone.
756 deadline = time.time() + 300
757 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
758 cls.sleep(0.01) # yield
759 if time.time() > deadline:
760 cls.logger.error("Timeout waiting for pg to stop")
761 break
762 for stamp, cap_name in cls._captures:
763 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100764 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200765
Damjan Marionf56b77a2016-10-03 19:44:57 +0200766 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200767 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200768 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100769 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200770
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100771 :param interfaces: iterable indexes of the interfaces.
772 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200773
Klement Sekeraf62ae122016-10-11 11:47:09 +0200774 """
775 result = []
776 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200777 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200778 setattr(cls, intf.name, intf)
779 result.append(intf)
780 cls.pg_interfaces = result
781 return result
782
Matej Klotton0178d522016-11-04 11:11:44 +0100783 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200784 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100785 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100786 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100787
Klement Sekerab9ef2732018-06-24 22:49:33 +0200788 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100789 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100790 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200791 result = [VppLoInterface(cls) for i in range(count)]
792 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100793 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100794 cls.lo_interfaces = result
795 return result
796
Neale Ranns192b13f2019-03-15 02:16:20 -0700797 @classmethod
798 def create_bvi_interfaces(cls, count):
799 """
800 Create BVI interfaces.
801
802 :param count: number of interfaces created.
803 :returns: List of created interfaces.
804 """
805 result = [VppBviInterface(cls) for i in range(count)]
806 for intf in result:
807 setattr(cls, intf.name, intf)
808 cls.bvi_interfaces = result
809 return result
810
Damjan Marionf56b77a2016-10-03 19:44:57 +0200811 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200812 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200813 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200814 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200815 NOTE: Currently works only when Raw layer is present.
816
817 :param packet: packet
818 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200819 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200820
821 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200822 packet_len = len(packet) + 4
823 extend = size - packet_len
824 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200825 num = (extend // len(padding)) + 1
826 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200827
Klement Sekeradab231a2016-12-21 08:50:14 +0100828 @classmethod
829 def reset_packet_infos(cls):
830 """ Reset the list of packet info objects and packet counts to zero """
831 cls._packet_infos = {}
832 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200833
Klement Sekeradab231a2016-12-21 08:50:14 +0100834 @classmethod
835 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200836 """
837 Create packet info object containing the source and destination indexes
838 and add it to the testcase's packet info list
839
Klement Sekeradab231a2016-12-21 08:50:14 +0100840 :param VppInterface src_if: source interface
841 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200842
843 :returns: _PacketInfo object
844
845 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200846 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100847 info.index = len(cls._packet_infos)
848 info.src = src_if.sw_if_index
849 info.dst = dst_if.sw_if_index
850 if isinstance(dst_if, VppSubInterface):
851 dst_idx = dst_if.parent.sw_if_index
852 else:
853 dst_idx = dst_if.sw_if_index
854 if dst_idx in cls._packet_count_for_dst_if_idx:
855 cls._packet_count_for_dst_if_idx[dst_idx] += 1
856 else:
857 cls._packet_count_for_dst_if_idx[dst_idx] = 1
858 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200859 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200860
Damjan Marionf56b77a2016-10-03 19:44:57 +0200861 @staticmethod
862 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200863 """
864 Convert _PacketInfo object to packet payload
865
866 :param info: _PacketInfo object
867
868 :returns: string containing serialized data from packet info
869 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100870 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
871 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200872
Damjan Marionf56b77a2016-10-03 19:44:57 +0200873 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800874 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200875 """
876 Convert packet payload to _PacketInfo object
877
878 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700879 :type payload: <class 'scapy.packet.Raw'>
880 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800881 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700882 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200883 :returns: _PacketInfo object containing de-serialized data from payload
884
885 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800886 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200887 info = _PacketInfo()
888 info.index = int(numbers[0])
889 info.src = int(numbers[1])
890 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100891 info.ip = int(numbers[3])
892 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200893 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200894
Damjan Marionf56b77a2016-10-03 19:44:57 +0200895 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200896 """
897 Iterate over the packet info list stored in the testcase
898 Start iteration with first element if info is None
899 Continue based on index in info if info is specified
900
901 :param info: info or None
902 :returns: next info in list or None if no more infos
903 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200904 if info is None:
905 next_index = 0
906 else:
907 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100908 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200909 return None
910 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100911 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200912
Klement Sekeraf62ae122016-10-11 11:47:09 +0200913 def get_next_packet_info_for_interface(self, src_index, info):
914 """
915 Search the packet info list for the next packet info with same source
916 interface index
917
918 :param src_index: source interface index to search for
919 :param info: packet info - where to start the search
920 :returns: packet info or None
921
922 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200923 while True:
924 info = self.get_next_packet_info(info)
925 if info is None:
926 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200927 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200928 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200929
Klement Sekeraf62ae122016-10-11 11:47:09 +0200930 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
931 """
932 Search the packet info list for the next packet info with same source
933 and destination interface indexes
934
935 :param src_index: source interface index to search for
936 :param dst_index: destination interface index to search for
937 :param info: packet info - where to start the search
938 :returns: packet info or None
939
940 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200941 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200942 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200943 if info is None:
944 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200945 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200946 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200947
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200948 def assert_equal(self, real_value, expected_value, name_or_class=None):
949 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100950 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200951 return
952 try:
953 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
954 msg = msg % (getdoc(name_or_class).strip(),
955 real_value, str(name_or_class(real_value)),
956 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100957 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200958 msg = "Invalid %s: %s does not match expected value %s" % (
959 name_or_class, real_value, expected_value)
960
961 self.assertEqual(real_value, expected_value, msg)
962
Klement Sekerab17dd962017-01-09 07:43:48 +0100963 def assert_in_range(self,
964 real_value,
965 expected_min,
966 expected_max,
967 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200968 if name is None:
969 msg = None
970 else:
971 msg = "Invalid %s: %s out of range <%s,%s>" % (
972 name, real_value, expected_min, expected_max)
973 self.assertTrue(expected_min <= real_value <= expected_max, msg)
974
Klement Sekerad81ae412018-05-16 10:52:54 +0200975 def assert_packet_checksums_valid(self, packet,
976 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700977 received = packet.__class__(scapy.compat.raw(packet))
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