blob: 67ac495547c13d2d33bbca8f424f4299c8174c8f [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
4import gc
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05005import logging
Paul Vinciguerra72f00042018-11-25 11:05:13 -08006import sys
Ole Trøan162989e2018-11-26 10:27:50 +00007import os
8import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04009import signal
Paul Vinciguerrad6f22172020-12-05 22:39:14 +000010import subprocess
Ole Trøan162989e2018-11-26 10:27:50 +000011import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +020012import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020013import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080014import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000015import random
16import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080017import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010018import platform
Ole Trøan162989e2018-11-26 10:27:50 +000019from collections import deque
20from threading import Thread, Event
21from inspect import getdoc, isclass
22from traceback import format_exception
23from logging import FileHandler, DEBUG, Formatter
Andrew Yourtchenko06f32812021-01-14 10:19:08 +000024from enum import Enum
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070025
26import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000027from scapy.packet import Raw
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040028import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080029from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010030from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000031from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070032from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000033from vpp_papi_provider import VppPapiProvider
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050034import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000035from vpp_papi.vpp_stats import VPPStats
Ole Troan4376ab22021-03-03 10:40:05 +010036from vpp_papi.vpp_transport_socket import VppTransportSocketIOError
Ole Trøan162989e2018-11-26 10:27:50 +000037from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
38 get_logger, colorize
39from vpp_object import VppObjectRegistry
40from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020041from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
42from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
43from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080044
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050045logger = logging.getLogger(__name__)
46
47# Set up an empty logger for the testcase that can be overridden as necessary
48null_logger = logging.getLogger('VppTestCase')
49null_logger.addHandler(logging.NullHandler())
50
juraj.linkescae64f82018-09-19 15:01:47 +020051PASS = 0
52FAIL = 1
53ERROR = 2
54SKIP = 3
55TEST_RUN = 4
56
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040057
58class BoolEnvironmentVariable(object):
59
60 def __init__(self, env_var_name, default='n', true_values=None):
61 self.name = env_var_name
62 self.default = default
63 self.true_values = true_values if true_values is not None else \
64 ("y", "yes", "1")
65
66 def __bool__(self):
67 return os.getenv(self.name, self.default).lower() in self.true_values
68
69 if sys.version_info[0] == 2:
70 __nonzero__ = __bool__
71
72 def __repr__(self):
73 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
74 (self.name, self.default, self.true_values)
75
76
77debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
78if debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010079 import debug_internal
80
Klement Sekeraf62ae122016-10-11 11:47:09 +020081"""
82 Test framework module.
83
84 The module provides a set of tools for constructing and running tests and
85 representing the results.
86"""
87
Klement Sekeraf62ae122016-10-11 11:47:09 +020088
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040089class VppDiedError(Exception):
90 """ exception for reporting that the subprocess has died."""
91
92 signals_by_value = {v: k for k, v in signal.__dict__.items() if
93 k.startswith('SIG') and not k.startswith('SIG_')}
94
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040095 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040096 self.rv = rv
97 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040098 self.testcase = testcase
99 self.method_name = method_name
100
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400101 try:
102 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400103 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400104 pass
105
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400106 if testcase is None and method_name is None:
107 in_msg = ''
108 else:
Klement Sekera79a31db2021-03-12 18:16:10 +0100109 in_msg = ' while running %s.%s' % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400110
Klement Sekera79a31db2021-03-12 18:16:10 +0100111 if self.rv:
112 msg = "VPP subprocess died unexpectedly%s with return code: %d%s."\
113 % (in_msg, self.rv, ' [%s]' %
114 (self.signal_name if
115 self.signal_name is not None else ''))
116 else:
117 msg = "VPP subprocess died unexpectedly%s." % in_msg
118
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]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100174 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100175 "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]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100192 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100193 "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
Dave Barachd498c9e2020-03-10 16:59:39 -0400219def _running_gcov_tests():
220 return BoolEnvironmentVariable("GCOV_TESTS")
221
222
223running_gcov_tests = _running_gcov_tests()
224
225
Klement Sekera909a6a12017-08-08 04:33:53 +0200226class KeepAliveReporter(object):
227 """
228 Singleton object which reports test start to parent process
229 """
230 _shared_state = {}
231
232 def __init__(self):
233 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800234 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200235
236 @property
237 def pipe(self):
238 return self._pipe
239
240 @pipe.setter
241 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800242 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200243 raise Exception("Internal error - pipe should only be set once.")
244 self._pipe = pipe
245
juraj.linkes40dd73b2018-09-21 13:55:16 +0200246 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200247 """
248 Write current test tmpdir & desc to keep-alive pipe to signal liveness
249 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200250 if self.pipe is None:
251 # if not running forked..
252 return
253
Klement Sekera909a6a12017-08-08 04:33:53 +0200254 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200255 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200256 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200257 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200258
Dave Wallacee2efd122017-09-30 22:04:21 -0400259 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200260
261
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000262class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000263 # marks the suites that must run at the end
264 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000265 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000266 # marks the suites broken on VPP multi-worker
267 FIXME_VPP_WORKERS = 2
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000268
269
270def create_tag_decorator(e):
271 def decorator(cls):
272 try:
273 cls.test_tags.append(e)
274 except AttributeError:
275 cls.test_tags = [e]
276 return cls
277 return decorator
278
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000279
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000280tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000281tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000282
283
Klement Sekerae2636852021-03-16 12:52:12 +0100284class DummyVpp:
285 returncode = None
286 pid = 0xcafebafe
287
288 def poll(self):
289 pass
290
291 def terminate(self):
292 pass
293
294
Damjan Marionf56b77a2016-10-03 19:44:57 +0200295class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100296 """This subclass is a base class for VPP test cases that are implemented as
297 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200298 """
299
Ole Troana45dc072018-12-21 16:04:22 +0100300 extra_vpp_punt_config = []
301 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500302 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400303 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100304
Klement Sekeraf62ae122016-10-11 11:47:09 +0200305 @property
306 def packet_infos(self):
307 """List of packet infos"""
308 return self._packet_infos
309
Klement Sekeradab231a2016-12-21 08:50:14 +0100310 @classmethod
311 def get_packet_count_for_if_idx(cls, dst_if_index):
312 """Get the number of packet info for specified destination if index"""
313 if dst_if_index in cls._packet_count_for_dst_if_idx:
314 return cls._packet_count_for_dst_if_idx[dst_if_index]
315 else:
316 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200317
318 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000319 def has_tag(cls, tag):
320 """ if the test case has a given tag - return true """
321 try:
322 return tag in cls.test_tags
323 except AttributeError:
324 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000325 return False
326
327 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000328 def is_tagged_run_solo(cls):
329 """ if the test case class is timing-sensitive - return true """
330 return cls.has_tag(TestCaseTag.RUN_SOLO)
331
332 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200333 def instance(cls):
334 """Return the instance of this testcase"""
335 return cls.test_instance
336
Damjan Marionf56b77a2016-10-03 19:44:57 +0200337 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200338 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000339 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200340 cls.debug_core = False
341 cls.debug_gdb = False
342 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000343 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100344 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200345 if d is None:
346 return
347 dl = d.lower()
348 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200349 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000350 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200351 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000352 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200353 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100354 elif dl == "attach":
355 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200356 else:
357 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000358 if dl == "gdb-all" or dl == "gdbserver-all":
359 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200360
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800361 @staticmethod
362 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200363 cpu_usage_list = [set(range(psutil.cpu_count()))]
364 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
365 if 'vpp_main' == p.info['name']]
366 for vpp_process in vpp_processes:
367 for cpu_usage_set in cpu_usage_list:
368 try:
369 cpu_num = vpp_process.cpu_num()
370 if cpu_num in cpu_usage_set:
371 cpu_usage_set_index = cpu_usage_list.index(
372 cpu_usage_set)
373 if cpu_usage_set_index == len(cpu_usage_list) - 1:
374 cpu_usage_list.append({cpu_num})
375 else:
376 cpu_usage_list[cpu_usage_set_index + 1].add(
377 cpu_num)
378 cpu_usage_set.remove(cpu_num)
379 break
380 except psutil.NoSuchProcess:
381 pass
382
383 for cpu_usage_set in cpu_usage_list:
384 if len(cpu_usage_set) > 0:
385 min_usage_set = cpu_usage_set
386 break
387
388 return random.choice(tuple(min_usage_set))
389
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800390 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200391 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200392 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400393 cls.step = BoolEnvironmentVariable('STEP')
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400394 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100395 c = os.getenv("CACHE_OUTPUT", "1")
396 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekerab8c72a42018-11-08 11:21:39 +0100397 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
398 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400399 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100400 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
401 plugin_path = None
402 if cls.plugin_path is not None:
403 if cls.extern_plugin_path is not None:
404 plugin_path = "%s:%s" % (
405 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100406 else:
407 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100408 elif cls.extern_plugin_path is not None:
409 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100410 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100411 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100412 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100413 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100414 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100415 if size is not None:
416 coredump_size = "coredump-size %s" % size
417 if coredump_size is None:
418 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200419
Ole Troana45dc072018-12-21 16:04:22 +0100420 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera8d815022021-03-15 16:58:10 +0100421 if not hasattr(cls, "vpp_worker_count"):
422 cls.vpp_worker_count = 0
423 worker_config = os.getenv("VPP_WORKER_CONFIG", "")
424 if worker_config:
425 elems = worker_config.split(" ")
426 if elems[0] != "workers" or len(elems) != 2:
427 raise ValueError("Wrong VPP_WORKER_CONFIG == '%s' value." %
428 worker_config)
429 cls.vpp_worker_count = int(elems[1])
430 if cls.vpp_worker_count > 0 and\
431 cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
432 cls.vpp_worker_count = 0
juraj.linkes184870a2018-07-16 14:22:01 +0200433
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000434 default_variant = os.getenv("VARIANT")
435 if default_variant is not None:
436 default_variant = "defaults { %s 100 }" % default_variant
437 else:
438 default_variant = ""
439
Dave Barach77841402020-04-29 17:04:10 -0400440 api_fuzzing = os.getenv("API_FUZZ")
441 if api_fuzzing is None:
442 api_fuzzing = 'off'
443
Klement Sekera8d815022021-03-15 16:58:10 +0100444 cls.vpp_cmdline = [
445 cls.vpp_bin,
446 "unix", "{", "nodaemon", debug_cli, "full-coredump",
447 coredump_size, "runtime-dir", cls.tempdir, "}",
448 "api-trace", "{", "on", "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100449 "api-segment", "{", "prefix", cls.get_api_segment_prefix(), "}",
Klement Sekera8d815022021-03-15 16:58:10 +0100450 "cpu", "{", "main-core", str(cpu_core_number), ]
451 if cls.vpp_worker_count:
452 cls.vpp_cmdline.extend(["workers", str(cls.vpp_worker_count)])
453 cls.vpp_cmdline.extend([
454 "}",
455 "physmem", "{", "max-size", "32m", "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100456 "statseg", "{", "socket-name", cls.get_stats_sock_path(), "}",
457 "socksvr", "{", "socket-name", cls.get_api_sock_path(), "}",
Klement Sekera8d815022021-03-15 16:58:10 +0100458 "node { ", default_variant, "}",
459 "api-fuzz {", api_fuzzing, "}",
460 "plugins", "{", "plugin", "dpdk_plugin.so", "{", "disable", "}",
461 "plugin", "rdma_plugin.so", "{", "disable", "}",
462 "plugin", "lisp_unittest_plugin.so", "{", "enable", "}",
463 "plugin", "unittest_plugin.so", "{", "enable", "}"
464 ] + cls.extra_vpp_plugin_config + ["}", ])
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000465
Ole Troana45dc072018-12-21 16:04:22 +0100466 if cls.extra_vpp_punt_config is not None:
467 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100468 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100469 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400470 if cls.test_plugin_path is not None:
471 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
472
Klement Sekerae2636852021-03-16 12:52:12 +0100473 if not cls.debug_attach:
474 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
475 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200476
477 @classmethod
478 def wait_for_enter(cls):
479 if cls.debug_gdbserver:
480 print(double_line_delim)
481 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
482 elif cls.debug_gdb:
483 print(double_line_delim)
484 print("Spawned VPP with PID: %d" % cls.vpp.pid)
485 else:
486 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
487 return
488 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000489 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200490 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400491 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000492 " -ex 'target remote localhost:{port}'"
493 .format(port=cls.gdbserver_port))
494 print("Now is the time to attach gdb by running the above "
495 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200496 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000497 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200498 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400499 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000500 print("Now is the time to attach gdb by running the above "
501 "command and set up breakpoints etc., then resume VPP from"
502 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200503 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800504 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200505
506 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100507 def attach_vpp(cls):
508 cls.vpp = DummyVpp()
509
510 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200511 def run_vpp(cls):
512 cmdline = cls.vpp_cmdline
513
514 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100515 gdbserver = '/usr/bin/gdbserver'
516 if not os.path.isfile(gdbserver) or \
517 not os.access(gdbserver, os.X_OK):
518 raise Exception("gdbserver binary '%s' does not exist or is "
519 "not executable" % gdbserver)
520
Dave Wallace24564332019-10-21 02:53:14 +0000521 cmdline = [gdbserver, 'localhost:{port}'
522 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200523 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
524
Klement Sekera931be3a2016-11-03 05:36:01 +0100525 try:
526 cls.vpp = subprocess.Popen(cmdline,
527 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100528 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800529 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800530 cls.logger.critical("Subprocess returned with non-0 return code: ("
531 "%s)", e.returncode)
532 raise
533 except OSError as e:
534 cls.logger.critical("Subprocess returned with OS error: "
535 "(%s) %s", e.errno, e.strerror)
536 raise
537 except Exception as e:
538 cls.logger.exception("Subprocess returned unexpected from "
539 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100540 raise
541
Klement Sekera277b89c2016-10-28 13:20:27 +0200542 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100543
Damjan Marionf56b77a2016-10-03 19:44:57 +0200544 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000545 def wait_for_coredump(cls):
546 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400547 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000548 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400549 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000550 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400551 ok = False
552 while time.time() < deadline:
553 cls.sleep(1)
554 size = curr_size
555 curr_size = os.path.getsize(corefile)
556 if size == curr_size:
557 ok = True
558 break
559 if not ok:
560 cls.logger.error("Timed out waiting for coredump to complete:"
561 " %s", corefile)
562 else:
563 cls.logger.error("Coredump complete: %s, size %d",
564 corefile, curr_size)
565
566 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100567 def get_stats_sock_path(cls):
568 return "%s/stats.sock" % cls.tempdir
569
570 @classmethod
571 def get_api_sock_path(cls):
572 return "%s/api.sock" % cls.tempdir
573
574 @classmethod
575 def get_api_segment_prefix(cls):
576 return os.path.basename(cls.tempdir) # Only used for VAPI
577
578 @classmethod
579 def get_tempdir(cls):
580 if cls.debug_attach:
581 return os.getenv("VPP_IN_GDB_TMP_DIR",
582 "/tmp/unittest-attach-gdb")
583 else:
584 return tempfile.mkdtemp(prefix='vpp-unittest-%s-' % cls.__name__)
585
586 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200587 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200588 """
589 Perform class setup before running the testcase
590 Remove shared memory files, start vpp and connect the vpp-api
591 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800592 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100593 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000594 seed = os.environ["RND_SEED"]
595 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100596 if hasattr(cls, 'parallel_handler'):
597 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100598 cls.logger.propagate = False
Klement Sekerae2636852021-03-16 12:52:12 +0100599 d = os.getenv("DEBUG", None)
600 cls.set_debug_flags(d)
601 cls.tempdir = cls.get_tempdir()
Klement Sekera027dbd52017-04-11 06:01:53 +0200602 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
603 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100604 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
605 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200606 cls.file_handler.setLevel(DEBUG)
607 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100608 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200609 os.chdir(cls.tempdir)
Ole Troan4376ab22021-03-03 10:40:05 +0100610 cls.logger.info("Temporary dir is %s, api socket is %s",
Klement Sekerae2636852021-03-16 12:52:12 +0100611 cls.tempdir, cls.get_api_sock_path())
612 cls.logger.debug("Random seed is %s", seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200613 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100614 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200615 cls._pcaps = []
616 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200617 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100618 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100619 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200620 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200621 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200622 # need to catch exceptions here because if we raise, then the cleanup
623 # doesn't get called and we might end with a zombie vpp
624 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100625 if cls.debug_attach:
626 cls.attach_vpp()
627 else:
628 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200629 cls.reporter.send_keep_alive(cls, 'setUpClass')
630 VppTestResult.current_test_case_info = TestCaseInfo(
631 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100632 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100633 cls.vpp_stderr_deque = deque()
Klement Sekerae2636852021-03-16 12:52:12 +0100634 if not cls.debug_attach:
635 cls.pump_thread_stop_flag = Event()
636 cls.pump_thread_wakeup_pipe = os.pipe()
637 cls.pump_thread = Thread(target=pump_output, args=(cls,))
638 cls.pump_thread.daemon = True
639 cls.pump_thread.start()
640 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400641 cls.vapi_response_timeout = 0
Ole Troan4376ab22021-03-03 10:40:05 +0100642 cls.vapi = VppPapiProvider(cls.__name__, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400643 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100644 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400645 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100646 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400647 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100648 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100649 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200650 try:
651 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100652 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200653 cls.vpp_startup_failed = True
654 cls.logger.critical(
655 "VPP died shortly after startup, check the"
656 " output to standard error for possible cause")
657 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100658 try:
659 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100660 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500661 cls.logger.debug("Exception connecting to vapi: %s" % e)
662 cls.vapi.disconnect()
663
Klement Sekera085f5c02016-11-24 01:59:16 +0100664 if cls.debug_gdbserver:
665 print(colorize("You're running VPP inside gdbserver but "
666 "VPP-API connection failed, did you forget "
667 "to 'continue' VPP from within gdb?", RED))
Ole Troan4376ab22021-03-03 10:40:05 +0100668 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100669 if cls.debug_attach:
670 last_line = cls.vapi.cli("show thread").split("\n")[-2]
671 cls.vpp_worker_count = int(last_line.split(" ")[0])
672 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400673 except vpp_papi.VPPRuntimeError as e:
674 cls.logger.debug("%s" % e)
675 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100676 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000677 except Exception as e:
678 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400679 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100680 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200681
Damjan Marionf56b77a2016-10-03 19:44:57 +0200682 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500683 def _debug_quit(cls):
684 if (cls.debug_gdbserver or cls.debug_gdb):
685 try:
686 cls.vpp.poll()
687
688 if cls.vpp.returncode is None:
689 print()
690 print(double_line_delim)
691 print("VPP or GDB server is still running")
692 print(single_line_delim)
693 input("When done debugging, press ENTER to kill the "
694 "process and finish running the testcase...")
695 except AttributeError:
696 pass
697
698 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200699 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200700 """
701 Disconnect vpp-api, kill vpp and cleanup shared memory files
702 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500703 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200704
juraj.linkes184870a2018-07-16 14:22:01 +0200705 # first signal that we want to stop the pump thread, then wake it up
706 if hasattr(cls, 'pump_thread_stop_flag'):
707 cls.pump_thread_stop_flag.set()
708 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100709 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100710 if hasattr(cls, 'pump_thread'):
711 cls.logger.debug("Waiting for pump thread to stop")
712 cls.pump_thread.join()
713 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500714 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100715 cls.vpp_stderr_reader_thread.join()
716
Klement Sekeraf62ae122016-10-11 11:47:09 +0200717 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100718 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100719 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700720 cls.logger.debug("Disconnecting class vapi client on %s",
721 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100722 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700723 cls.logger.debug("Deleting class vapi attribute on %s",
724 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100725 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200726 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100727 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000728 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100729 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400730 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100731 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100732 try:
733 outs, errs = cls.vpp.communicate(timeout=5)
734 except subprocess.TimeoutExpired:
735 cls.vpp.kill()
736 outs, errs = cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700737 cls.logger.debug("Deleting class vpp attribute on %s",
738 cls.__name__)
Klement Sekerae2636852021-03-16 12:52:12 +0100739 if not cls.debug_attach:
740 cls.vpp.stdout.close()
741 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200742 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200743
Klement Sekera3747c752017-04-10 06:30:17 +0200744 if cls.vpp_startup_failed:
745 stdout_log = cls.logger.info
746 stderr_log = cls.logger.critical
747 else:
748 stdout_log = cls.logger.info
749 stderr_log = cls.logger.info
750
Klement Sekerae4504c62016-12-08 10:16:41 +0100751 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200752 stdout_log(single_line_delim)
753 stdout_log('VPP output to stdout while running %s:', cls.__name__)
754 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100755 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200756 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
757 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200758 stdout_log('\n%s', vpp_output)
759 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200760
Klement Sekerae4504c62016-12-08 10:16:41 +0100761 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200762 stderr_log(single_line_delim)
763 stderr_log('VPP output to stderr while running %s:', cls.__name__)
764 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100765 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200766 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
767 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200768 stderr_log('\n%s', vpp_output)
769 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200770
Damjan Marionf56b77a2016-10-03 19:44:57 +0200771 @classmethod
772 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200773 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700774 cls.logger.debug("--- tearDownClass() for %s called ---" %
775 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200776 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200777 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200778 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100779 cls.reset_packet_infos()
780 if debug_framework:
781 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200782
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700783 def show_commands_at_teardown(self):
784 """ Allow subclass specific teardown logging additions."""
785 self.logger.info("--- No test specific show commands provided. ---")
786
Damjan Marionf56b77a2016-10-03 19:44:57 +0200787 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200788 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100789 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
790 (self.__class__.__name__, self._testMethodName,
791 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700792
793 try:
794 if not self.vpp_dead:
795 self.logger.debug(self.vapi.cli("show trace max 1000"))
796 self.logger.info(self.vapi.ppcli("show interface"))
797 self.logger.info(self.vapi.ppcli("show hardware"))
798 self.logger.info(self.statistics.set_errors_str())
799 self.logger.info(self.vapi.ppcli("show run"))
800 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400801 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700802 self.logger.info("Logging testcase specific show commands.")
803 self.show_commands_at_teardown()
804 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500805 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000806 m = self._testMethodName
807 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500808 tmp_api_trace = "/tmp/%s" % api_trace
809 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
810 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
811 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
812 vpp_api_trace_log))
813 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500814 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500815 vpp_api_trace_log))
Ole Troan4376ab22021-03-03 10:40:05 +0100816 except VppTransportSocketIOError:
817 self.logger.debug("VppTransportSocketIOError: Vpp dead. "
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700818 "Cannot log show commands.")
819 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100820 else:
821 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200822
Damjan Marionf56b77a2016-10-03 19:44:57 +0200823 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200824 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800825 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200826 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100827 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400828 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
829 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100830 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100831 self.vpp_stdout_deque.append(
832 "--- test setUp() for %s.%s(%s) starts here ---\n" %
833 (self.__class__.__name__, self._testMethodName,
834 self._testMethodDoc))
835 self.vpp_stderr_deque.append(
836 "--- test setUp() for %s.%s(%s) starts here ---\n" %
837 (self.__class__.__name__, self._testMethodName,
838 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200839 self.vapi.cli("clear trace")
840 # store the test instance inside the test class - so that objects
841 # holding the class can access instance methods (like assertEqual)
842 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200843
Damjan Marionf56b77a2016-10-03 19:44:57 +0200844 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200845 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200846 """
847 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200848
Klement Sekera75e7d132017-09-20 08:26:30 +0200849 :param interfaces: iterable interface indexes (if None,
850 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200851
Klement Sekeraf62ae122016-10-11 11:47:09 +0200852 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200853 if interfaces is None:
854 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200855 for i in interfaces:
856 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200857
Damjan Marionf56b77a2016-10-03 19:44:57 +0200858 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200859 def register_pcap(cls, intf, worker):
860 """ Register a pcap in the testclass """
Klement Sekera9225dee2016-12-12 08:36:58 +0100861 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200862 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100863
864 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000865 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400866 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
867 # returns float("2.190522")
868 timestr = cls.vapi.cli('show clock')
869 head, sep, tail = timestr.partition(',')
870 head, sep, tail = head.partition('Time now')
871 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000872
873 @classmethod
874 def sleep_on_vpp_time(cls, sec):
875 """ Sleep according to time in VPP world """
876 # On a busy system with many processes
877 # we might end up with VPP time being slower than real world
878 # So take that into account when waiting for VPP to do something
879 start_time = cls.get_vpp_time()
880 while cls.get_vpp_time() - start_time < sec:
881 cls.sleep(0.1)
882
883 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100884 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000885 """ Enable the PG, wait till it is done, then clean up """
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200886 for (intf, worker) in cls._old_pcaps:
887 intf.rename_old_pcap_file(intf.get_in_path(worker),
888 intf.in_history_counter)
889 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100890 if trace:
891 cls.vapi.cli("clear trace")
892 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200893 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000894 # PG, when starts, runs to completion -
895 # so let's avoid a race condition,
896 # and wait a little till it's done.
897 # Then clean it up - and then be gone.
898 deadline = time.time() + 300
899 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
900 cls.sleep(0.01) # yield
901 if time.time() > deadline:
902 cls.logger.error("Timeout waiting for pg to stop")
903 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200904 for intf, worker in cls._pcaps:
Klement Sekera7ba9fae2021-03-31 13:36:38 +0200905 cls.vapi.cli('packet-generator delete %s' %
906 intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200907 cls._old_pcaps = cls._pcaps
908 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200909
Damjan Marionf56b77a2016-10-03 19:44:57 +0200910 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200911 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200912 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100913 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200914
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100915 :param interfaces: iterable indexes of the interfaces.
916 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200917
Klement Sekeraf62ae122016-10-11 11:47:09 +0200918 """
919 result = []
920 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200921 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200922 setattr(cls, intf.name, intf)
923 result.append(intf)
924 cls.pg_interfaces = result
925 return result
926
Matej Klotton0178d522016-11-04 11:11:44 +0100927 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200928 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100929 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100930 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100931
Klement Sekerab9ef2732018-06-24 22:49:33 +0200932 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100933 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100934 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200935 result = [VppLoInterface(cls) for i in range(count)]
936 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100937 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100938 cls.lo_interfaces = result
939 return result
940
Neale Ranns192b13f2019-03-15 02:16:20 -0700941 @classmethod
942 def create_bvi_interfaces(cls, count):
943 """
944 Create BVI interfaces.
945
946 :param count: number of interfaces created.
947 :returns: List of created interfaces.
948 """
949 result = [VppBviInterface(cls) for i in range(count)]
950 for intf in result:
951 setattr(cls, intf.name, intf)
952 cls.bvi_interfaces = result
953 return result
954
Damjan Marionf56b77a2016-10-03 19:44:57 +0200955 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200956 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200957 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200958 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200959 NOTE: Currently works only when Raw layer is present.
960
961 :param packet: packet
962 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200963 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200964
965 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200966 packet_len = len(packet) + 4
967 extend = size - packet_len
968 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200969 num = (extend // len(padding)) + 1
970 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200971
Klement Sekeradab231a2016-12-21 08:50:14 +0100972 @classmethod
973 def reset_packet_infos(cls):
974 """ Reset the list of packet info objects and packet counts to zero """
975 cls._packet_infos = {}
976 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200977
Klement Sekeradab231a2016-12-21 08:50:14 +0100978 @classmethod
979 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200980 """
981 Create packet info object containing the source and destination indexes
982 and add it to the testcase's packet info list
983
Klement Sekeradab231a2016-12-21 08:50:14 +0100984 :param VppInterface src_if: source interface
985 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200986
987 :returns: _PacketInfo object
988
989 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200990 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100991 info.index = len(cls._packet_infos)
992 info.src = src_if.sw_if_index
993 info.dst = dst_if.sw_if_index
994 if isinstance(dst_if, VppSubInterface):
995 dst_idx = dst_if.parent.sw_if_index
996 else:
997 dst_idx = dst_if.sw_if_index
998 if dst_idx in cls._packet_count_for_dst_if_idx:
999 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1000 else:
1001 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1002 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001003 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001004
Damjan Marionf56b77a2016-10-03 19:44:57 +02001005 @staticmethod
1006 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001007 """
1008 Convert _PacketInfo object to packet payload
1009
1010 :param info: _PacketInfo object
1011
1012 :returns: string containing serialized data from packet info
1013 """
Pavel Kotucek59dda062017-03-02 15:22:47 +01001014 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
1015 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001016
Damjan Marionf56b77a2016-10-03 19:44:57 +02001017 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001018 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001019 """
1020 Convert packet payload to _PacketInfo object
1021
1022 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001023 :type payload: <class 'scapy.packet.Raw'>
1024 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001025 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001026 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001027 :returns: _PacketInfo object containing de-serialized data from payload
1028
1029 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001030 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001031 info = _PacketInfo()
1032 info.index = int(numbers[0])
1033 info.src = int(numbers[1])
1034 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +01001035 info.ip = int(numbers[3])
1036 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +02001037 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001038
Damjan Marionf56b77a2016-10-03 19:44:57 +02001039 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001040 """
1041 Iterate over the packet info list stored in the testcase
1042 Start iteration with first element if info is None
1043 Continue based on index in info if info is specified
1044
1045 :param info: info or None
1046 :returns: next info in list or None if no more infos
1047 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001048 if info is None:
1049 next_index = 0
1050 else:
1051 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001052 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001053 return None
1054 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001055 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001056
Klement Sekeraf62ae122016-10-11 11:47:09 +02001057 def get_next_packet_info_for_interface(self, src_index, info):
1058 """
1059 Search the packet info list for the next packet info with same source
1060 interface index
1061
1062 :param src_index: source interface index to search for
1063 :param info: packet info - where to start the search
1064 :returns: packet info or None
1065
1066 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001067 while True:
1068 info = self.get_next_packet_info(info)
1069 if info is None:
1070 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001071 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001072 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001073
Klement Sekeraf62ae122016-10-11 11:47:09 +02001074 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1075 """
1076 Search the packet info list for the next packet info with same source
1077 and destination interface indexes
1078
1079 :param src_index: source interface index to search for
1080 :param dst_index: destination interface index to search for
1081 :param info: packet info - where to start the search
1082 :returns: packet info or None
1083
1084 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001085 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001086 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001087 if info is None:
1088 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001089 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001090 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001091
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001092 def assert_equal(self, real_value, expected_value, name_or_class=None):
1093 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001094 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001095 return
1096 try:
1097 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1098 msg = msg % (getdoc(name_or_class).strip(),
1099 real_value, str(name_or_class(real_value)),
1100 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001101 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001102 msg = "Invalid %s: %s does not match expected value %s" % (
1103 name_or_class, real_value, expected_value)
1104
1105 self.assertEqual(real_value, expected_value, msg)
1106
Klement Sekerab17dd962017-01-09 07:43:48 +01001107 def assert_in_range(self,
1108 real_value,
1109 expected_min,
1110 expected_max,
1111 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001112 if name is None:
1113 msg = None
1114 else:
1115 msg = "Invalid %s: %s out of range <%s,%s>" % (
1116 name, real_value, expected_min, expected_max)
1117 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1118
Klement Sekerad81ae412018-05-16 10:52:54 +02001119 def assert_packet_checksums_valid(self, packet,
1120 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001121 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001122 udp_layers = ['UDP', 'UDPerror']
1123 checksum_fields = ['cksum', 'chksum']
1124 checksums = []
1125 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001126 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001127 while True:
1128 layer = temp.getlayer(counter)
1129 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001130 layer = layer.copy()
1131 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001132 for cf in checksum_fields:
1133 if hasattr(layer, cf):
1134 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001135 0 == getattr(layer, cf) and \
1136 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001137 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001138 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001139 checksums.append((counter, cf))
1140 else:
1141 break
1142 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001143 if 0 == len(checksums):
1144 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001145 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001146 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001147 calc_sum = getattr(temp[layer], cf)
1148 self.assert_equal(
1149 getattr(received[layer], cf), calc_sum,
1150 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1151 self.logger.debug(
1152 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1153 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001154
1155 def assert_checksum_valid(self, received_packet, layer,
1156 field_name='chksum',
1157 ignore_zero_checksum=False):
1158 """ Check checksum of received packet on given layer """
1159 received_packet_checksum = getattr(received_packet[layer], field_name)
1160 if ignore_zero_checksum and 0 == received_packet_checksum:
1161 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001162 recalculated = received_packet.__class__(
1163 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001164 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001165 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001166 self.assert_equal(received_packet_checksum,
1167 getattr(recalculated[layer], field_name),
1168 "packet checksum on layer: %s" % layer)
1169
1170 def assert_ip_checksum_valid(self, received_packet,
1171 ignore_zero_checksum=False):
1172 self.assert_checksum_valid(received_packet, 'IP',
1173 ignore_zero_checksum=ignore_zero_checksum)
1174
1175 def assert_tcp_checksum_valid(self, received_packet,
1176 ignore_zero_checksum=False):
1177 self.assert_checksum_valid(received_packet, 'TCP',
1178 ignore_zero_checksum=ignore_zero_checksum)
1179
1180 def assert_udp_checksum_valid(self, received_packet,
1181 ignore_zero_checksum=True):
1182 self.assert_checksum_valid(received_packet, 'UDP',
1183 ignore_zero_checksum=ignore_zero_checksum)
1184
1185 def assert_embedded_icmp_checksum_valid(self, received_packet):
1186 if received_packet.haslayer(IPerror):
1187 self.assert_checksum_valid(received_packet, 'IPerror')
1188 if received_packet.haslayer(TCPerror):
1189 self.assert_checksum_valid(received_packet, 'TCPerror')
1190 if received_packet.haslayer(UDPerror):
1191 self.assert_checksum_valid(received_packet, 'UDPerror',
1192 ignore_zero_checksum=True)
1193 if received_packet.haslayer(ICMPerror):
1194 self.assert_checksum_valid(received_packet, 'ICMPerror')
1195
1196 def assert_icmp_checksum_valid(self, received_packet):
1197 self.assert_checksum_valid(received_packet, 'ICMP')
1198 self.assert_embedded_icmp_checksum_valid(received_packet)
1199
1200 def assert_icmpv6_checksum_valid(self, pkt):
1201 if pkt.haslayer(ICMPv6DestUnreach):
1202 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1203 self.assert_embedded_icmp_checksum_valid(pkt)
1204 if pkt.haslayer(ICMPv6EchoRequest):
1205 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1206 if pkt.haslayer(ICMPv6EchoReply):
1207 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1208
Klement Sekera3a343d42019-05-16 14:35:46 +02001209 def get_packet_counter(self, counter):
1210 if counter.startswith("/"):
1211 counter_value = self.statistics.get_counter(counter)
1212 else:
1213 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001214 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001215 for i in range(1, len(counters) - 1):
1216 results = counters[i].split()
1217 if results[1] == counter:
1218 counter_value = int(results[0])
1219 break
1220 return counter_value
1221
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001222 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001223 counter_value = self.get_packet_counter(counter)
1224 self.assert_equal(counter_value, expected_value,
1225 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001226
Ole Troan233e4682019-05-16 15:01:34 +02001227 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001228 counter_value = self.statistics[counter].sum()
Ole Troan233e4682019-05-16 15:01:34 +02001229 self.assert_equal(counter_value, expected_value,
1230 "error counter `%s'" % counter)
1231
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001232 @classmethod
1233 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001234
1235 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1236 # * by Guido, only the main thread can be interrupted.
1237 # */
1238 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1239 if timeout == 0:
1240 # yield quantum
1241 if hasattr(os, 'sched_yield'):
1242 os.sched_yield()
1243 else:
1244 time.sleep(0)
1245 return
1246
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001247 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001248 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001249 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001250 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001251 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001252 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001253 "slept for %es instead of ~%es!",
1254 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001255
1256 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001257 "Finished sleep (%s) - slept %es (wanted %es)",
1258 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001259
Benoît Ganne8c45e512021-02-19 16:39:13 +01001260 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001261 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001262 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001263 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001264
1265 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1266 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001267 if not timeout:
1268 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001269 for i in self.pg_interfaces:
1270 i.get_capture(0, timeout=timeout)
1271 i.assert_nothing_captured(remark=remark)
1272 timeout = 0.1
1273
Benoît Ganne8c45e512021-02-19 16:39:13 +01001274 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1275 trace=True):
Neale Rannsd7603d92019-03-28 08:56:10 +00001276 if not n_rx:
1277 n_rx = len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001278 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001279 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001280 return rx
1281
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001282 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1283 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001284 rx = output.get_capture(len(pkts))
1285 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001286 if not timeout:
1287 timeout = 1
1288 for i in self.pg_interfaces:
1289 if i not in outputs:
1290 i.get_capture(0, timeout=timeout)
1291 i.assert_nothing_captured()
1292 timeout = 0.1
1293
Neale Ranns52fae862018-01-08 04:41:42 -08001294 return rx
1295
Damjan Marionf56b77a2016-10-03 19:44:57 +02001296
juraj.linkes184870a2018-07-16 14:22:01 +02001297def get_testcase_doc_name(test):
1298 return getdoc(test.__class__).splitlines()[0]
1299
1300
Ole Trøan5ba91592018-11-22 10:01:09 +00001301def get_test_description(descriptions, test):
1302 short_description = test.shortDescription()
1303 if descriptions and short_description:
1304 return short_description
1305 else:
1306 return str(test)
1307
1308
juraj.linkes40dd73b2018-09-21 13:55:16 +02001309class TestCaseInfo(object):
1310 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1311 self.logger = logger
1312 self.tempdir = tempdir
1313 self.vpp_pid = vpp_pid
1314 self.vpp_bin_path = vpp_bin_path
1315 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001316
1317
Damjan Marionf56b77a2016-10-03 19:44:57 +02001318class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001319 """
1320 @property result_string
1321 String variable to store the test case result string.
1322 @property errors
1323 List variable containing 2-tuples of TestCase instances and strings
1324 holding formatted tracebacks. Each tuple represents a test which
1325 raised an unexpected exception.
1326 @property failures
1327 List variable containing 2-tuples of TestCase instances and strings
1328 holding formatted tracebacks. Each tuple represents a test where
1329 a failure was explicitly signalled using the TestCase.assert*()
1330 methods.
1331 """
1332
juraj.linkes40dd73b2018-09-21 13:55:16 +02001333 failed_test_cases_info = set()
1334 core_crash_test_cases_info = set()
1335 current_test_case_info = None
1336
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001337 def __init__(self, stream=None, descriptions=None, verbosity=None,
1338 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001339 """
Klement Sekerada505f62017-01-04 12:58:53 +01001340 :param stream File descriptor to store where to report test results.
1341 Set to the standard error stream by default.
1342 :param descriptions Boolean variable to store information if to use
1343 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001344 :param verbosity Integer variable to store required verbosity level.
1345 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001346 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001347 self.stream = stream
1348 self.descriptions = descriptions
1349 self.verbosity = verbosity
1350 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001351 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001352
Damjan Marionf56b77a2016-10-03 19:44:57 +02001353 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001354 """
1355 Record a test succeeded result
1356
1357 :param test:
1358
1359 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001360 if self.current_test_case_info:
1361 self.current_test_case_info.logger.debug(
1362 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1363 test._testMethodName,
1364 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001365 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001366 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001367
juraj.linkescae64f82018-09-19 15:01:47 +02001368 self.send_result_through_pipe(test, PASS)
1369
Klement Sekeraf62ae122016-10-11 11:47:09 +02001370 def addSkip(self, test, reason):
1371 """
1372 Record a test skipped.
1373
1374 :param test:
1375 :param reason:
1376
1377 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001378 if self.current_test_case_info:
1379 self.current_test_case_info.logger.debug(
1380 "--- addSkip() %s.%s(%s) called, reason is %s" %
1381 (test.__class__.__name__, test._testMethodName,
1382 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001383 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001384 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001385
juraj.linkescae64f82018-09-19 15:01:47 +02001386 self.send_result_through_pipe(test, SKIP)
1387
juraj.linkes40dd73b2018-09-21 13:55:16 +02001388 def symlink_failed(self):
1389 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001390 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001391 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001392 link_path = os.path.join(
1393 failed_dir,
1394 '%s-FAILED' %
1395 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001396
1397 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001398 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001399 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001400 "os.symlink(%s, %s)" %
1401 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001402 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001403 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001404 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001405 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001406 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001407
Klement Sekeraf413bef2017-08-15 07:09:02 +02001408 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001409 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001410
juraj.linkescae64f82018-09-19 15:01:47 +02001411 def send_result_through_pipe(self, test, result):
1412 if hasattr(self, 'test_framework_result_pipe'):
1413 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001414 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001415 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001416
juraj.linkes40dd73b2018-09-21 13:55:16 +02001417 def log_error(self, test, err, fn_name):
1418 if self.current_test_case_info:
1419 if isinstance(test, unittest.suite._ErrorHolder):
1420 test_name = test.description
1421 else:
1422 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1423 test._testMethodName,
1424 test._testMethodDoc)
1425 self.current_test_case_info.logger.debug(
1426 "--- %s() %s called, err is %s" %
1427 (fn_name, test_name, err))
1428 self.current_test_case_info.logger.debug(
1429 "formatted exception is:\n%s" %
1430 "".join(format_exception(*err)))
1431
1432 def add_error(self, test, err, unittest_fn, error_type):
1433 if error_type == FAIL:
1434 self.log_error(test, err, 'addFailure')
1435 error_type_str = colorize("FAIL", RED)
1436 elif error_type == ERROR:
1437 self.log_error(test, err, 'addError')
1438 error_type_str = colorize("ERROR", RED)
1439 else:
1440 raise Exception('Error type %s cannot be used to record an '
1441 'error or a failure' % error_type)
1442
1443 unittest_fn(self, test, err)
1444 if self.current_test_case_info:
1445 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1446 (error_type_str,
1447 self.current_test_case_info.tempdir)
1448 self.symlink_failed()
1449 self.failed_test_cases_info.add(self.current_test_case_info)
1450 if is_core_present(self.current_test_case_info.tempdir):
1451 if not self.current_test_case_info.core_crash_test:
1452 if isinstance(test, unittest.suite._ErrorHolder):
1453 test_name = str(test)
1454 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001455 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001456 get_testcase_doc_name(test), test.id())
1457 self.current_test_case_info.core_crash_test = test_name
1458 self.core_crash_test_cases_info.add(
1459 self.current_test_case_info)
1460 else:
1461 self.result_string = '%s [no temp dir]' % error_type_str
1462
1463 self.send_result_through_pipe(test, error_type)
1464
Damjan Marionf56b77a2016-10-03 19:44:57 +02001465 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001466 """
1467 Record a test failed result
1468
1469 :param test:
1470 :param err: error message
1471
1472 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001473 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001474
Damjan Marionf56b77a2016-10-03 19:44:57 +02001475 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001476 """
1477 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001478
Klement Sekeraf62ae122016-10-11 11:47:09 +02001479 :param test:
1480 :param err: error message
1481
1482 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001483 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001484
Damjan Marionf56b77a2016-10-03 19:44:57 +02001485 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001486 """
1487 Get test description
1488
1489 :param test:
1490 :returns: test description
1491
1492 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001493 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001494
Damjan Marionf56b77a2016-10-03 19:44:57 +02001495 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001496 """
1497 Start a test
1498
1499 :param test:
1500
1501 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001502
1503 def print_header(test):
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001504 test_doc = getdoc(test)
1505 if not test_doc:
1506 raise Exception("No doc string for test '%s'" % test.id())
1507 test_title = test_doc.splitlines()[0]
1508 test_title_colored = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001509 if test.is_tagged_run_solo():
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001510 # long live PEP-8 and 80 char width limitation...
1511 c = YELLOW
1512 test_title_colored = colorize("SOLO RUN: " + test_title, c)
1513
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001514 # This block may overwrite the colorized title above,
1515 # but we want this to stand out and be fixed
1516 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
1517 c = RED
1518 w = "FIXME with VPP workers: "
1519 test_title_colored = colorize(w + test_title, c)
1520
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001521 if not hasattr(test.__class__, '_header_printed'):
1522 print(double_line_delim)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001523 print(test_title_colored)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001524 print(double_line_delim)
1525 test.__class__._header_printed = True
1526
1527 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001528 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001529 unittest.TestResult.startTest(self, test)
1530 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001531 self.stream.writeln(
1532 "Starting " + self.getDescription(test) + " ...")
1533 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001534
Damjan Marionf56b77a2016-10-03 19:44:57 +02001535 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001536 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001537 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001538
1539 :param test:
1540
1541 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001542 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001543
Damjan Marionf56b77a2016-10-03 19:44:57 +02001544 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001545 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001546 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001547 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001548 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001549 else:
Ole Troan0c629322019-11-28 14:48:44 +01001550 self.stream.writeln("%-68s %4.2f %s" %
1551 (self.getDescription(test),
1552 time.time() - self.start_test,
1553 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001554
1555 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001556
Damjan Marionf56b77a2016-10-03 19:44:57 +02001557 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001558 """
1559 Print errors from running the test case
1560 """
juraj.linkesabec0122018-11-16 17:28:56 +01001561 if len(self.errors) > 0 or len(self.failures) > 0:
1562 self.stream.writeln()
1563 self.printErrorList('ERROR', self.errors)
1564 self.printErrorList('FAIL', self.failures)
1565
1566 # ^^ that is the last output from unittest before summary
1567 if not self.runner.print_summary:
1568 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1569 self.stream = devnull
1570 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001571
Damjan Marionf56b77a2016-10-03 19:44:57 +02001572 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001573 """
1574 Print error list to the output stream together with error type
1575 and test case description.
1576
1577 :param flavour: error type
1578 :param errors: iterable errors
1579
1580 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001581 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001582 self.stream.writeln(double_line_delim)
1583 self.stream.writeln("%s: %s" %
1584 (flavour, self.getDescription(test)))
1585 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001586 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001587
1588
Damjan Marionf56b77a2016-10-03 19:44:57 +02001589class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001590 """
Klement Sekera104543f2017-02-03 07:29:43 +01001591 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001592 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001593
Klement Sekeraf62ae122016-10-11 11:47:09 +02001594 @property
1595 def resultclass(self):
1596 """Class maintaining the results of the tests"""
1597 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001598
juraj.linkes184870a2018-07-16 14:22:01 +02001599 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001600 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001601 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001602 # ignore stream setting here, use hard-coded stdout to be in sync
1603 # with prints from VppTestCase methods ...
1604 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1605 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001606 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001607 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001608
juraj.linkesabec0122018-11-16 17:28:56 +01001609 self.orig_stream = self.stream
1610 self.resultclass.test_framework_result_pipe = result_pipe
1611
1612 self.print_summary = print_summary
1613
1614 def _makeResult(self):
1615 return self.resultclass(self.stream,
1616 self.descriptions,
1617 self.verbosity,
1618 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001619
Damjan Marionf56b77a2016-10-03 19:44:57 +02001620 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001621 """
1622 Run the tests
1623
1624 :param test:
1625
1626 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001627 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001628
1629 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001630 if not self.print_summary:
1631 self.stream = self.orig_stream
1632 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001633 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001634
1635
1636class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001637 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1638 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001639 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001640 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001641 if hasattr(self, 'testcase') and self.testcase.debug_all:
1642 if self.testcase.debug_gdbserver:
1643 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1644 .format(port=self.testcase.gdbserver_port)] + args
1645 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1646 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001647 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001648 self.app_name = os.path.basename(self.app_bin)
1649 if hasattr(self, 'role'):
1650 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001651 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001652 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001653 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001654 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001655
Dave Wallace24564332019-10-21 02:53:14 +00001656 def wait_for_enter(self):
1657 if not hasattr(self, 'testcase'):
1658 return
1659 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1660 print()
1661 print(double_line_delim)
1662 print("Spawned GDB Server for '{app}' with PID: {pid}"
1663 .format(app=self.app_name, pid=self.process.pid))
1664 elif self.testcase.debug_all and self.testcase.debug_gdb:
1665 print()
1666 print(double_line_delim)
1667 print("Spawned '{app}' with PID: {pid}"
1668 .format(app=self.app_name, pid=self.process.pid))
1669 else:
1670 return
1671 print(single_line_delim)
1672 print("You can debug '{app}' using:".format(app=self.app_name))
1673 if self.testcase.debug_gdbserver:
1674 print("sudo gdb " + self.app_bin +
1675 " -ex 'target remote localhost:{port}'"
1676 .format(port=self.testcase.gdbserver_port))
1677 print("Now is the time to attach gdb by running the above "
1678 "command, set up breakpoints etc., then resume from "
1679 "within gdb by issuing the 'continue' command")
1680 self.testcase.gdbserver_port += 1
1681 elif self.testcase.debug_gdb:
1682 print("sudo gdb " + self.app_bin +
1683 " -ex 'attach {pid}'".format(pid=self.process.pid))
1684 print("Now is the time to attach gdb by running the above "
1685 "command and set up breakpoints etc., then resume from"
1686 " within gdb by issuing the 'continue' command")
1687 print(single_line_delim)
1688 input("Press ENTER to continue running the testcase...")
1689
Neale Ranns812ed392017-10-16 04:20:13 -07001690 def run(self):
1691 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001692 if not os.path.exists(executable) or not os.access(
1693 executable, os.F_OK | os.X_OK):
1694 # Exit code that means some system file did not exist,
1695 # could not be opened, or had some other kind of error.
1696 self.result = os.EX_OSFILE
1697 raise EnvironmentError(
1698 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001699 self.logger.debug("Running executable: '{app}'"
1700 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001701 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001702 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001703 env["CK_LOG_FILE_NAME"] = "-"
1704 self.process = subprocess.Popen(
1705 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1706 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001707 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001708 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001709 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001710 self.logger.info("Return code is `%s'" % self.process.returncode)
1711 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001712 self.logger.info("Executable `{app}' wrote to stdout:"
1713 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001714 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001715 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001716 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001717 self.logger.info("Executable `{app}' wrote to stderr:"
1718 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001719 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001720 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001721 self.logger.info(single_line_delim)
1722 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001723
Klement Sekera6aa58b72019-05-16 14:34:55 +02001724
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001725if __name__ == '__main__':
1726 pass