blob: 4ccbc454b5bf4ba8e6831664b3919b9525865969 [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
Paul Vinciguerra499ea642019-03-15 09:39:19 -070036from vpp_papi.vpp_transport_shmem import VppTransportShmemIOError
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
Damjan Marionf56b77a2016-10-03 19:44:57 +0200284class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100285 """This subclass is a base class for VPP test cases that are implemented as
286 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200287 """
288
Ole Troana45dc072018-12-21 16:04:22 +0100289 extra_vpp_punt_config = []
290 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500291 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400292 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100293
Klement Sekeraf62ae122016-10-11 11:47:09 +0200294 @property
295 def packet_infos(self):
296 """List of packet infos"""
297 return self._packet_infos
298
Klement Sekeradab231a2016-12-21 08:50:14 +0100299 @classmethod
300 def get_packet_count_for_if_idx(cls, dst_if_index):
301 """Get the number of packet info for specified destination if index"""
302 if dst_if_index in cls._packet_count_for_dst_if_idx:
303 return cls._packet_count_for_dst_if_idx[dst_if_index]
304 else:
305 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200306
307 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000308 def has_tag(cls, tag):
309 """ if the test case has a given tag - return true """
310 try:
311 return tag in cls.test_tags
312 except AttributeError:
313 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000314 return False
315
316 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000317 def is_tagged_run_solo(cls):
318 """ if the test case class is timing-sensitive - return true """
319 return cls.has_tag(TestCaseTag.RUN_SOLO)
320
321 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200322 def instance(cls):
323 """Return the instance of this testcase"""
324 return cls.test_instance
325
Damjan Marionf56b77a2016-10-03 19:44:57 +0200326 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200327 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000328 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200329 cls.debug_core = False
330 cls.debug_gdb = False
331 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000332 cls.debug_all = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200333 if d is None:
334 return
335 dl = d.lower()
336 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200337 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000338 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200339 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000340 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200341 cls.debug_gdbserver = True
342 else:
343 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000344 if dl == "gdb-all" or dl == "gdbserver-all":
345 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200346
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800347 @staticmethod
348 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200349 cpu_usage_list = [set(range(psutil.cpu_count()))]
350 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
351 if 'vpp_main' == p.info['name']]
352 for vpp_process in vpp_processes:
353 for cpu_usage_set in cpu_usage_list:
354 try:
355 cpu_num = vpp_process.cpu_num()
356 if cpu_num in cpu_usage_set:
357 cpu_usage_set_index = cpu_usage_list.index(
358 cpu_usage_set)
359 if cpu_usage_set_index == len(cpu_usage_list) - 1:
360 cpu_usage_list.append({cpu_num})
361 else:
362 cpu_usage_list[cpu_usage_set_index + 1].add(
363 cpu_num)
364 cpu_usage_set.remove(cpu_num)
365 break
366 except psutil.NoSuchProcess:
367 pass
368
369 for cpu_usage_set in cpu_usage_list:
370 if len(cpu_usage_set) > 0:
371 min_usage_set = cpu_usage_set
372 break
373
374 return random.choice(tuple(min_usage_set))
375
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800376 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200377 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200378 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400379 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100380 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400381 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100382 c = os.getenv("CACHE_OUTPUT", "1")
383 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200384 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100385 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
386 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400387 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100388 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
389 plugin_path = None
390 if cls.plugin_path is not None:
391 if cls.extern_plugin_path is not None:
392 plugin_path = "%s:%s" % (
393 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100394 else:
395 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100396 elif cls.extern_plugin_path is not None:
397 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100398 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100399 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100400 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100401 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100402 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100403 if size is not None:
404 coredump_size = "coredump-size %s" % size
405 if coredump_size is None:
406 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200407
Ole Troana45dc072018-12-21 16:04:22 +0100408 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000409 if not hasattr(cls, "worker_config"):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000410 cls.worker_config = os.getenv("VPP_WORKER_CONFIG", "")
411 if cls.worker_config != "":
412 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
413 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200414
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000415 default_variant = os.getenv("VARIANT")
416 if default_variant is not None:
417 default_variant = "defaults { %s 100 }" % default_variant
418 else:
419 default_variant = ""
420
Dave Barach77841402020-04-29 17:04:10 -0400421 api_fuzzing = os.getenv("API_FUZZ")
422 if api_fuzzing is None:
423 api_fuzzing = 'off'
424
Ole Troana45dc072018-12-21 16:04:22 +0100425 cls.vpp_cmdline = [cls.vpp_bin, "unix",
426 "{", "nodaemon", debug_cli, "full-coredump",
427 coredump_size, "runtime-dir", cls.tempdir, "}",
428 "api-trace", "{", "on", "}", "api-segment", "{",
429 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000430 "main-core", str(cpu_core_number),
431 cls.worker_config, "}",
Dave Barach4ed25982019-12-25 09:24:58 -0500432 "physmem", "{", "max-size", "32m", "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200433 "statseg", "{", "socket-name", cls.stats_sock, "}",
434 "socksvr", "{", "socket-name", cls.api_sock, "}",
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000435 "node { ", default_variant, "}",
Dave Barach77841402020-04-29 17:04:10 -0400436 "api-fuzz {", api_fuzzing, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200437 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100438 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200439 "}", "plugin", "rdma_plugin.so", "{", "disable",
Neale Ranns2b202bc2020-09-21 08:17:51 +0000440 "}", "plugin", "lisp_unittest_plugin.so", "{",
441 "enable",
Ole Troana45dc072018-12-21 16:04:22 +0100442 "}", "plugin", "unittest_plugin.so", "{", "enable",
443 "}"] + cls.extra_vpp_plugin_config + ["}", ]
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000444
Ole Troana45dc072018-12-21 16:04:22 +0100445 if cls.extra_vpp_punt_config is not None:
446 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100447 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100448 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400449 if cls.test_plugin_path is not None:
450 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
451
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100452 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
453 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200454
455 @classmethod
456 def wait_for_enter(cls):
457 if cls.debug_gdbserver:
458 print(double_line_delim)
459 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
460 elif cls.debug_gdb:
461 print(double_line_delim)
462 print("Spawned VPP with PID: %d" % cls.vpp.pid)
463 else:
464 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
465 return
466 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000467 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200468 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400469 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000470 " -ex 'target remote localhost:{port}'"
471 .format(port=cls.gdbserver_port))
472 print("Now is the time to attach gdb by running the above "
473 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200474 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000475 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200476 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400477 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000478 print("Now is the time to attach gdb by running the above "
479 "command and set up breakpoints etc., then resume VPP from"
480 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200481 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800482 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200483
484 @classmethod
485 def run_vpp(cls):
486 cmdline = cls.vpp_cmdline
487
488 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100489 gdbserver = '/usr/bin/gdbserver'
490 if not os.path.isfile(gdbserver) or \
491 not os.access(gdbserver, os.X_OK):
492 raise Exception("gdbserver binary '%s' does not exist or is "
493 "not executable" % gdbserver)
494
Dave Wallace24564332019-10-21 02:53:14 +0000495 cmdline = [gdbserver, 'localhost:{port}'
496 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200497 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
498
Klement Sekera931be3a2016-11-03 05:36:01 +0100499 try:
500 cls.vpp = subprocess.Popen(cmdline,
501 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100502 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800503 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800504 cls.logger.critical("Subprocess returned with non-0 return code: ("
505 "%s)", e.returncode)
506 raise
507 except OSError as e:
508 cls.logger.critical("Subprocess returned with OS error: "
509 "(%s) %s", e.errno, e.strerror)
510 raise
511 except Exception as e:
512 cls.logger.exception("Subprocess returned unexpected from "
513 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100514 raise
515
Klement Sekera277b89c2016-10-28 13:20:27 +0200516 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100517
Damjan Marionf56b77a2016-10-03 19:44:57 +0200518 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000519 def wait_for_coredump(cls):
520 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400521 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000522 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400523 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000524 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400525 ok = False
526 while time.time() < deadline:
527 cls.sleep(1)
528 size = curr_size
529 curr_size = os.path.getsize(corefile)
530 if size == curr_size:
531 ok = True
532 break
533 if not ok:
534 cls.logger.error("Timed out waiting for coredump to complete:"
535 " %s", corefile)
536 else:
537 cls.logger.error("Coredump complete: %s, size %d",
538 corefile, curr_size)
539
540 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200541 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200542 """
543 Perform class setup before running the testcase
544 Remove shared memory files, start vpp and connect the vpp-api
545 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800546 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100547 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100548 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000549 seed = os.environ["RND_SEED"]
550 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100551 if hasattr(cls, 'parallel_handler'):
552 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100553 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700554
Klement Sekeraf62ae122016-10-11 11:47:09 +0200555 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200556 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200557 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200558 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200559 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
560 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100561 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
562 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200563 cls.file_handler.setLevel(DEBUG)
564 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700565 cls.logger.debug("--- setUpClass() for %s called ---" %
566 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200567 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200568 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200569 cls.logger.info("Temporary dir is %s, shm prefix is %s",
570 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000571 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200572 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100573 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100574 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200575 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100576 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100577 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200578 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200579 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200580 # need to catch exceptions here because if we raise, then the cleanup
581 # doesn't get called and we might end with a zombie vpp
582 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200583 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200584 cls.reporter.send_keep_alive(cls, 'setUpClass')
585 VppTestResult.current_test_case_info = TestCaseInfo(
586 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100587 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100588 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100589 cls.pump_thread_stop_flag = Event()
590 cls.pump_thread_wakeup_pipe = os.pipe()
591 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100592 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100593 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200594 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400595 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200596 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400597 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100598 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400599 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100600 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400601 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100602 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200603 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200604 try:
605 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100606 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200607 cls.vpp_startup_failed = True
608 cls.logger.critical(
609 "VPP died shortly after startup, check the"
610 " output to standard error for possible cause")
611 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100612 try:
613 cls.vapi.connect()
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500614 except vpp_papi.VPPIOError as e:
615 cls.logger.debug("Exception connecting to vapi: %s" % e)
616 cls.vapi.disconnect()
617
Klement Sekera085f5c02016-11-24 01:59:16 +0100618 if cls.debug_gdbserver:
619 print(colorize("You're running VPP inside gdbserver but "
620 "VPP-API connection failed, did you forget "
621 "to 'continue' VPP from within gdb?", RED))
622 raise
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400623 except vpp_papi.VPPRuntimeError as e:
624 cls.logger.debug("%s" % e)
625 cls.quit()
626 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000627 except Exception as e:
628 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400629 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100630 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200631
Damjan Marionf56b77a2016-10-03 19:44:57 +0200632 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500633 def _debug_quit(cls):
634 if (cls.debug_gdbserver or cls.debug_gdb):
635 try:
636 cls.vpp.poll()
637
638 if cls.vpp.returncode is None:
639 print()
640 print(double_line_delim)
641 print("VPP or GDB server is still running")
642 print(single_line_delim)
643 input("When done debugging, press ENTER to kill the "
644 "process and finish running the testcase...")
645 except AttributeError:
646 pass
647
648 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200649 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200650 """
651 Disconnect vpp-api, kill vpp and cleanup shared memory files
652 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500653 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200654
juraj.linkes184870a2018-07-16 14:22:01 +0200655 # first signal that we want to stop the pump thread, then wake it up
656 if hasattr(cls, 'pump_thread_stop_flag'):
657 cls.pump_thread_stop_flag.set()
658 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100659 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100660 if hasattr(cls, 'pump_thread'):
661 cls.logger.debug("Waiting for pump thread to stop")
662 cls.pump_thread.join()
663 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500664 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100665 cls.vpp_stderr_reader_thread.join()
666
Klement Sekeraf62ae122016-10-11 11:47:09 +0200667 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100668 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100669 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700670 cls.logger.debug("Disconnecting class vapi client on %s",
671 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100672 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700673 cls.logger.debug("Deleting class vapi attribute on %s",
674 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100675 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200676 cls.vpp.poll()
677 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000678 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100679 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400680 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100681 cls.logger.debug("Waiting for vpp to die")
682 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700683 cls.logger.debug("Deleting class vpp attribute on %s",
684 cls.__name__)
Paul Vinciguerrad1f05f72020-12-16 21:03:16 -0500685 cls.vpp.stdout.close()
686 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200687 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200688
Klement Sekera3747c752017-04-10 06:30:17 +0200689 if cls.vpp_startup_failed:
690 stdout_log = cls.logger.info
691 stderr_log = cls.logger.critical
692 else:
693 stdout_log = cls.logger.info
694 stderr_log = cls.logger.info
695
Klement Sekerae4504c62016-12-08 10:16:41 +0100696 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200697 stdout_log(single_line_delim)
698 stdout_log('VPP output to stdout while running %s:', cls.__name__)
699 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100700 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200701 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
702 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200703 stdout_log('\n%s', vpp_output)
704 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200705
Klement Sekerae4504c62016-12-08 10:16:41 +0100706 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200707 stderr_log(single_line_delim)
708 stderr_log('VPP output to stderr while running %s:', cls.__name__)
709 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100710 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200711 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
712 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200713 stderr_log('\n%s', vpp_output)
714 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200715
Damjan Marionf56b77a2016-10-03 19:44:57 +0200716 @classmethod
717 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200718 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700719 cls.logger.debug("--- tearDownClass() for %s called ---" %
720 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200721 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200722 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200723 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100724 cls.reset_packet_infos()
725 if debug_framework:
726 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700728 def show_commands_at_teardown(self):
729 """ Allow subclass specific teardown logging additions."""
730 self.logger.info("--- No test specific show commands provided. ---")
731
Damjan Marionf56b77a2016-10-03 19:44:57 +0200732 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200733 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100734 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
735 (self.__class__.__name__, self._testMethodName,
736 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700737
738 try:
739 if not self.vpp_dead:
740 self.logger.debug(self.vapi.cli("show trace max 1000"))
741 self.logger.info(self.vapi.ppcli("show interface"))
742 self.logger.info(self.vapi.ppcli("show hardware"))
743 self.logger.info(self.statistics.set_errors_str())
744 self.logger.info(self.vapi.ppcli("show run"))
745 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400746 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700747 self.logger.info("Logging testcase specific show commands.")
748 self.show_commands_at_teardown()
749 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500750 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000751 m = self._testMethodName
752 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500753 tmp_api_trace = "/tmp/%s" % api_trace
754 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
755 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
756 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
757 vpp_api_trace_log))
758 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500759 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500760 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700761 except VppTransportShmemIOError:
762 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
763 "Cannot log show commands.")
764 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100765 else:
766 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200767
Damjan Marionf56b77a2016-10-03 19:44:57 +0200768 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200769 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800770 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200771 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100772 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400773
774 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
775 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100776 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100777 self.vpp_stdout_deque.append(
778 "--- test setUp() for %s.%s(%s) starts here ---\n" %
779 (self.__class__.__name__, self._testMethodName,
780 self._testMethodDoc))
781 self.vpp_stderr_deque.append(
782 "--- test setUp() for %s.%s(%s) starts here ---\n" %
783 (self.__class__.__name__, self._testMethodName,
784 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200785 self.vapi.cli("clear trace")
786 # store the test instance inside the test class - so that objects
787 # holding the class can access instance methods (like assertEqual)
788 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200789
Damjan Marionf56b77a2016-10-03 19:44:57 +0200790 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200791 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200792 """
793 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200794
Klement Sekera75e7d132017-09-20 08:26:30 +0200795 :param interfaces: iterable interface indexes (if None,
796 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200797
Klement Sekeraf62ae122016-10-11 11:47:09 +0200798 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200799 if interfaces is None:
800 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200801 for i in interfaces:
802 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200803
Damjan Marionf56b77a2016-10-03 19:44:57 +0200804 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100805 def register_capture(cls, cap_name):
806 """ Register a capture in the testclass """
807 # add to the list of captures with current timestamp
808 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100809
810 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000811 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400812 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
813 # returns float("2.190522")
814 timestr = cls.vapi.cli('show clock')
815 head, sep, tail = timestr.partition(',')
816 head, sep, tail = head.partition('Time now')
817 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000818
819 @classmethod
820 def sleep_on_vpp_time(cls, sec):
821 """ Sleep according to time in VPP world """
822 # On a busy system with many processes
823 # we might end up with VPP time being slower than real world
824 # So take that into account when waiting for VPP to do something
825 start_time = cls.get_vpp_time()
826 while cls.get_vpp_time() - start_time < sec:
827 cls.sleep(0.1)
828
829 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100830 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000831 """ Enable the PG, wait till it is done, then clean up """
Benoît Ganne8c45e512021-02-19 16:39:13 +0100832 if trace:
833 cls.vapi.cli("clear trace")
834 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200835 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000836 # PG, when starts, runs to completion -
837 # so let's avoid a race condition,
838 # and wait a little till it's done.
839 # Then clean it up - and then be gone.
840 deadline = time.time() + 300
841 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
842 cls.sleep(0.01) # yield
843 if time.time() > deadline:
844 cls.logger.error("Timeout waiting for pg to stop")
845 break
846 for stamp, cap_name in cls._captures:
847 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100848 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200849
Damjan Marionf56b77a2016-10-03 19:44:57 +0200850 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200851 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200852 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100853 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200854
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100855 :param interfaces: iterable indexes of the interfaces.
856 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200857
Klement Sekeraf62ae122016-10-11 11:47:09 +0200858 """
859 result = []
860 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200861 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200862 setattr(cls, intf.name, intf)
863 result.append(intf)
864 cls.pg_interfaces = result
865 return result
866
Matej Klotton0178d522016-11-04 11:11:44 +0100867 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200868 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100869 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100870 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100871
Klement Sekerab9ef2732018-06-24 22:49:33 +0200872 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100873 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100874 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200875 result = [VppLoInterface(cls) for i in range(count)]
876 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100877 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100878 cls.lo_interfaces = result
879 return result
880
Neale Ranns192b13f2019-03-15 02:16:20 -0700881 @classmethod
882 def create_bvi_interfaces(cls, count):
883 """
884 Create BVI interfaces.
885
886 :param count: number of interfaces created.
887 :returns: List of created interfaces.
888 """
889 result = [VppBviInterface(cls) for i in range(count)]
890 for intf in result:
891 setattr(cls, intf.name, intf)
892 cls.bvi_interfaces = result
893 return result
894
Damjan Marionf56b77a2016-10-03 19:44:57 +0200895 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200896 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200897 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200898 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200899 NOTE: Currently works only when Raw layer is present.
900
901 :param packet: packet
902 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200903 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200904
905 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200906 packet_len = len(packet) + 4
907 extend = size - packet_len
908 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200909 num = (extend // len(padding)) + 1
910 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200911
Klement Sekeradab231a2016-12-21 08:50:14 +0100912 @classmethod
913 def reset_packet_infos(cls):
914 """ Reset the list of packet info objects and packet counts to zero """
915 cls._packet_infos = {}
916 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200917
Klement Sekeradab231a2016-12-21 08:50:14 +0100918 @classmethod
919 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200920 """
921 Create packet info object containing the source and destination indexes
922 and add it to the testcase's packet info list
923
Klement Sekeradab231a2016-12-21 08:50:14 +0100924 :param VppInterface src_if: source interface
925 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200926
927 :returns: _PacketInfo object
928
929 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200930 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100931 info.index = len(cls._packet_infos)
932 info.src = src_if.sw_if_index
933 info.dst = dst_if.sw_if_index
934 if isinstance(dst_if, VppSubInterface):
935 dst_idx = dst_if.parent.sw_if_index
936 else:
937 dst_idx = dst_if.sw_if_index
938 if dst_idx in cls._packet_count_for_dst_if_idx:
939 cls._packet_count_for_dst_if_idx[dst_idx] += 1
940 else:
941 cls._packet_count_for_dst_if_idx[dst_idx] = 1
942 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200943 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200944
Damjan Marionf56b77a2016-10-03 19:44:57 +0200945 @staticmethod
946 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200947 """
948 Convert _PacketInfo object to packet payload
949
950 :param info: _PacketInfo object
951
952 :returns: string containing serialized data from packet info
953 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100954 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
955 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200956
Damjan Marionf56b77a2016-10-03 19:44:57 +0200957 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800958 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200959 """
960 Convert packet payload to _PacketInfo object
961
962 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700963 :type payload: <class 'scapy.packet.Raw'>
964 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800965 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700966 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200967 :returns: _PacketInfo object containing de-serialized data from payload
968
969 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800970 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200971 info = _PacketInfo()
972 info.index = int(numbers[0])
973 info.src = int(numbers[1])
974 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100975 info.ip = int(numbers[3])
976 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200977 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200978
Damjan Marionf56b77a2016-10-03 19:44:57 +0200979 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200980 """
981 Iterate over the packet info list stored in the testcase
982 Start iteration with first element if info is None
983 Continue based on index in info if info is specified
984
985 :param info: info or None
986 :returns: next info in list or None if no more infos
987 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200988 if info is None:
989 next_index = 0
990 else:
991 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100992 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200993 return None
994 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100995 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200996
Klement Sekeraf62ae122016-10-11 11:47:09 +0200997 def get_next_packet_info_for_interface(self, src_index, info):
998 """
999 Search the packet info list for the next packet info with same source
1000 interface index
1001
1002 :param src_index: source interface index to search for
1003 :param info: packet info - where to start the search
1004 :returns: packet info or None
1005
1006 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001007 while True:
1008 info = self.get_next_packet_info(info)
1009 if info is None:
1010 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001011 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001012 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001013
Klement Sekeraf62ae122016-10-11 11:47:09 +02001014 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1015 """
1016 Search the packet info list for the next packet info with same source
1017 and destination interface indexes
1018
1019 :param src_index: source interface index to search for
1020 :param dst_index: destination interface index to search for
1021 :param info: packet info - where to start the search
1022 :returns: packet info or None
1023
1024 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001025 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001026 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001027 if info is None:
1028 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001029 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001030 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001031
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001032 def assert_equal(self, real_value, expected_value, name_or_class=None):
1033 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001034 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001035 return
1036 try:
1037 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1038 msg = msg % (getdoc(name_or_class).strip(),
1039 real_value, str(name_or_class(real_value)),
1040 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001041 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001042 msg = "Invalid %s: %s does not match expected value %s" % (
1043 name_or_class, real_value, expected_value)
1044
1045 self.assertEqual(real_value, expected_value, msg)
1046
Klement Sekerab17dd962017-01-09 07:43:48 +01001047 def assert_in_range(self,
1048 real_value,
1049 expected_min,
1050 expected_max,
1051 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001052 if name is None:
1053 msg = None
1054 else:
1055 msg = "Invalid %s: %s out of range <%s,%s>" % (
1056 name, real_value, expected_min, expected_max)
1057 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1058
Klement Sekerad81ae412018-05-16 10:52:54 +02001059 def assert_packet_checksums_valid(self, packet,
1060 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001061 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001062 udp_layers = ['UDP', 'UDPerror']
1063 checksum_fields = ['cksum', 'chksum']
1064 checksums = []
1065 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001066 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001067 while True:
1068 layer = temp.getlayer(counter)
1069 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001070 layer = layer.copy()
1071 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001072 for cf in checksum_fields:
1073 if hasattr(layer, cf):
1074 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001075 0 == getattr(layer, cf) and \
1076 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001077 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001078 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001079 checksums.append((counter, cf))
1080 else:
1081 break
1082 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001083 if 0 == len(checksums):
1084 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001085 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001086 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001087 calc_sum = getattr(temp[layer], cf)
1088 self.assert_equal(
1089 getattr(received[layer], cf), calc_sum,
1090 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1091 self.logger.debug(
1092 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1093 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001094
1095 def assert_checksum_valid(self, received_packet, layer,
1096 field_name='chksum',
1097 ignore_zero_checksum=False):
1098 """ Check checksum of received packet on given layer """
1099 received_packet_checksum = getattr(received_packet[layer], field_name)
1100 if ignore_zero_checksum and 0 == received_packet_checksum:
1101 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001102 recalculated = received_packet.__class__(
1103 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001104 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001105 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001106 self.assert_equal(received_packet_checksum,
1107 getattr(recalculated[layer], field_name),
1108 "packet checksum on layer: %s" % layer)
1109
1110 def assert_ip_checksum_valid(self, received_packet,
1111 ignore_zero_checksum=False):
1112 self.assert_checksum_valid(received_packet, 'IP',
1113 ignore_zero_checksum=ignore_zero_checksum)
1114
1115 def assert_tcp_checksum_valid(self, received_packet,
1116 ignore_zero_checksum=False):
1117 self.assert_checksum_valid(received_packet, 'TCP',
1118 ignore_zero_checksum=ignore_zero_checksum)
1119
1120 def assert_udp_checksum_valid(self, received_packet,
1121 ignore_zero_checksum=True):
1122 self.assert_checksum_valid(received_packet, 'UDP',
1123 ignore_zero_checksum=ignore_zero_checksum)
1124
1125 def assert_embedded_icmp_checksum_valid(self, received_packet):
1126 if received_packet.haslayer(IPerror):
1127 self.assert_checksum_valid(received_packet, 'IPerror')
1128 if received_packet.haslayer(TCPerror):
1129 self.assert_checksum_valid(received_packet, 'TCPerror')
1130 if received_packet.haslayer(UDPerror):
1131 self.assert_checksum_valid(received_packet, 'UDPerror',
1132 ignore_zero_checksum=True)
1133 if received_packet.haslayer(ICMPerror):
1134 self.assert_checksum_valid(received_packet, 'ICMPerror')
1135
1136 def assert_icmp_checksum_valid(self, received_packet):
1137 self.assert_checksum_valid(received_packet, 'ICMP')
1138 self.assert_embedded_icmp_checksum_valid(received_packet)
1139
1140 def assert_icmpv6_checksum_valid(self, pkt):
1141 if pkt.haslayer(ICMPv6DestUnreach):
1142 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1143 self.assert_embedded_icmp_checksum_valid(pkt)
1144 if pkt.haslayer(ICMPv6EchoRequest):
1145 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1146 if pkt.haslayer(ICMPv6EchoReply):
1147 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1148
Klement Sekera3a343d42019-05-16 14:35:46 +02001149 def get_packet_counter(self, counter):
1150 if counter.startswith("/"):
1151 counter_value = self.statistics.get_counter(counter)
1152 else:
1153 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001154 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001155 for i in range(1, len(counters) - 1):
1156 results = counters[i].split()
1157 if results[1] == counter:
1158 counter_value = int(results[0])
1159 break
1160 return counter_value
1161
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001162 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001163 counter_value = self.get_packet_counter(counter)
1164 self.assert_equal(counter_value, expected_value,
1165 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001166
Ole Troan233e4682019-05-16 15:01:34 +02001167 def assert_error_counter_equal(self, counter, expected_value):
1168 counter_value = self.statistics.get_err_counter(counter)
1169 self.assert_equal(counter_value, expected_value,
1170 "error counter `%s'" % counter)
1171
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001172 @classmethod
1173 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001174
1175 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1176 # * by Guido, only the main thread can be interrupted.
1177 # */
1178 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1179 if timeout == 0:
1180 # yield quantum
1181 if hasattr(os, 'sched_yield'):
1182 os.sched_yield()
1183 else:
1184 time.sleep(0)
1185 return
1186
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001187 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001188 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001189 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001190 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001191 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001192 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001193 "slept for %es instead of ~%es!",
1194 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001195
1196 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001197 "Finished sleep (%s) - slept %es (wanted %es)",
1198 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001199
Benoît Ganne8c45e512021-02-19 16:39:13 +01001200 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001201 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001202 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001203 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001204
1205 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1206 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001207 if not timeout:
1208 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001209 for i in self.pg_interfaces:
1210 i.get_capture(0, timeout=timeout)
1211 i.assert_nothing_captured(remark=remark)
1212 timeout = 0.1
1213
Benoît Ganne8c45e512021-02-19 16:39:13 +01001214 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1215 trace=True):
Neale Rannsd7603d92019-03-28 08:56:10 +00001216 if not n_rx:
1217 n_rx = len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001218 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001219 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001220 return rx
1221
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001222 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1223 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001224 rx = output.get_capture(len(pkts))
1225 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001226 if not timeout:
1227 timeout = 1
1228 for i in self.pg_interfaces:
1229 if i not in outputs:
1230 i.get_capture(0, timeout=timeout)
1231 i.assert_nothing_captured()
1232 timeout = 0.1
1233
Neale Ranns52fae862018-01-08 04:41:42 -08001234 return rx
1235
Damjan Marionf56b77a2016-10-03 19:44:57 +02001236
juraj.linkes184870a2018-07-16 14:22:01 +02001237def get_testcase_doc_name(test):
1238 return getdoc(test.__class__).splitlines()[0]
1239
1240
Ole Trøan5ba91592018-11-22 10:01:09 +00001241def get_test_description(descriptions, test):
1242 short_description = test.shortDescription()
1243 if descriptions and short_description:
1244 return short_description
1245 else:
1246 return str(test)
1247
1248
juraj.linkes40dd73b2018-09-21 13:55:16 +02001249class TestCaseInfo(object):
1250 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1251 self.logger = logger
1252 self.tempdir = tempdir
1253 self.vpp_pid = vpp_pid
1254 self.vpp_bin_path = vpp_bin_path
1255 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001256
1257
Damjan Marionf56b77a2016-10-03 19:44:57 +02001258class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001259 """
1260 @property result_string
1261 String variable to store the test case result string.
1262 @property errors
1263 List variable containing 2-tuples of TestCase instances and strings
1264 holding formatted tracebacks. Each tuple represents a test which
1265 raised an unexpected exception.
1266 @property failures
1267 List variable containing 2-tuples of TestCase instances and strings
1268 holding formatted tracebacks. Each tuple represents a test where
1269 a failure was explicitly signalled using the TestCase.assert*()
1270 methods.
1271 """
1272
juraj.linkes40dd73b2018-09-21 13:55:16 +02001273 failed_test_cases_info = set()
1274 core_crash_test_cases_info = set()
1275 current_test_case_info = None
1276
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001277 def __init__(self, stream=None, descriptions=None, verbosity=None,
1278 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001279 """
Klement Sekerada505f62017-01-04 12:58:53 +01001280 :param stream File descriptor to store where to report test results.
1281 Set to the standard error stream by default.
1282 :param descriptions Boolean variable to store information if to use
1283 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001284 :param verbosity Integer variable to store required verbosity level.
1285 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001286 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001287 self.stream = stream
1288 self.descriptions = descriptions
1289 self.verbosity = verbosity
1290 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001291 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001292
Damjan Marionf56b77a2016-10-03 19:44:57 +02001293 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001294 """
1295 Record a test succeeded result
1296
1297 :param test:
1298
1299 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001300 if self.current_test_case_info:
1301 self.current_test_case_info.logger.debug(
1302 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1303 test._testMethodName,
1304 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001305 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001306 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001307
juraj.linkescae64f82018-09-19 15:01:47 +02001308 self.send_result_through_pipe(test, PASS)
1309
Klement Sekeraf62ae122016-10-11 11:47:09 +02001310 def addSkip(self, test, reason):
1311 """
1312 Record a test skipped.
1313
1314 :param test:
1315 :param reason:
1316
1317 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001318 if self.current_test_case_info:
1319 self.current_test_case_info.logger.debug(
1320 "--- addSkip() %s.%s(%s) called, reason is %s" %
1321 (test.__class__.__name__, test._testMethodName,
1322 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001323 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001324 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001325
juraj.linkescae64f82018-09-19 15:01:47 +02001326 self.send_result_through_pipe(test, SKIP)
1327
juraj.linkes40dd73b2018-09-21 13:55:16 +02001328 def symlink_failed(self):
1329 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001330 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001331 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001332 link_path = os.path.join(
1333 failed_dir,
1334 '%s-FAILED' %
1335 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001336
1337 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001338 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001339 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001340 "os.symlink(%s, %s)" %
1341 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001342 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001343 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001344 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001345 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001346 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001347
Klement Sekeraf413bef2017-08-15 07:09:02 +02001348 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001349 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001350
juraj.linkescae64f82018-09-19 15:01:47 +02001351 def send_result_through_pipe(self, test, result):
1352 if hasattr(self, 'test_framework_result_pipe'):
1353 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001354 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001355 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001356
juraj.linkes40dd73b2018-09-21 13:55:16 +02001357 def log_error(self, test, err, fn_name):
1358 if self.current_test_case_info:
1359 if isinstance(test, unittest.suite._ErrorHolder):
1360 test_name = test.description
1361 else:
1362 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1363 test._testMethodName,
1364 test._testMethodDoc)
1365 self.current_test_case_info.logger.debug(
1366 "--- %s() %s called, err is %s" %
1367 (fn_name, test_name, err))
1368 self.current_test_case_info.logger.debug(
1369 "formatted exception is:\n%s" %
1370 "".join(format_exception(*err)))
1371
1372 def add_error(self, test, err, unittest_fn, error_type):
1373 if error_type == FAIL:
1374 self.log_error(test, err, 'addFailure')
1375 error_type_str = colorize("FAIL", RED)
1376 elif error_type == ERROR:
1377 self.log_error(test, err, 'addError')
1378 error_type_str = colorize("ERROR", RED)
1379 else:
1380 raise Exception('Error type %s cannot be used to record an '
1381 'error or a failure' % error_type)
1382
1383 unittest_fn(self, test, err)
1384 if self.current_test_case_info:
1385 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1386 (error_type_str,
1387 self.current_test_case_info.tempdir)
1388 self.symlink_failed()
1389 self.failed_test_cases_info.add(self.current_test_case_info)
1390 if is_core_present(self.current_test_case_info.tempdir):
1391 if not self.current_test_case_info.core_crash_test:
1392 if isinstance(test, unittest.suite._ErrorHolder):
1393 test_name = str(test)
1394 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001395 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001396 get_testcase_doc_name(test), test.id())
1397 self.current_test_case_info.core_crash_test = test_name
1398 self.core_crash_test_cases_info.add(
1399 self.current_test_case_info)
1400 else:
1401 self.result_string = '%s [no temp dir]' % error_type_str
1402
1403 self.send_result_through_pipe(test, error_type)
1404
Damjan Marionf56b77a2016-10-03 19:44:57 +02001405 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001406 """
1407 Record a test failed result
1408
1409 :param test:
1410 :param err: error message
1411
1412 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001413 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001414
Damjan Marionf56b77a2016-10-03 19:44:57 +02001415 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001416 """
1417 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001418
Klement Sekeraf62ae122016-10-11 11:47:09 +02001419 :param test:
1420 :param err: error message
1421
1422 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001423 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001424
Damjan Marionf56b77a2016-10-03 19:44:57 +02001425 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001426 """
1427 Get test description
1428
1429 :param test:
1430 :returns: test description
1431
1432 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001433 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001434
Damjan Marionf56b77a2016-10-03 19:44:57 +02001435 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001436 """
1437 Start a test
1438
1439 :param test:
1440
1441 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001442
1443 def print_header(test):
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001444 test_doc = getdoc(test)
1445 if not test_doc:
1446 raise Exception("No doc string for test '%s'" % test.id())
1447 test_title = test_doc.splitlines()[0]
1448 test_title_colored = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001449 if test.is_tagged_run_solo():
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001450 # long live PEP-8 and 80 char width limitation...
1451 c = YELLOW
1452 test_title_colored = colorize("SOLO RUN: " + test_title, c)
1453
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001454 # This block may overwrite the colorized title above,
1455 # but we want this to stand out and be fixed
1456 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
1457 c = RED
1458 w = "FIXME with VPP workers: "
1459 test_title_colored = colorize(w + test_title, c)
1460
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001461 if not hasattr(test.__class__, '_header_printed'):
1462 print(double_line_delim)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001463 print(test_title_colored)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001464 print(double_line_delim)
1465 test.__class__._header_printed = True
1466
1467 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001468 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001469 unittest.TestResult.startTest(self, test)
1470 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001471 self.stream.writeln(
1472 "Starting " + self.getDescription(test) + " ...")
1473 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001474
Damjan Marionf56b77a2016-10-03 19:44:57 +02001475 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001476 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001477 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001478
1479 :param test:
1480
1481 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001482 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001483
Damjan Marionf56b77a2016-10-03 19:44:57 +02001484 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001485 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001486 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001487 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001488 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001489 else:
Ole Troan0c629322019-11-28 14:48:44 +01001490 self.stream.writeln("%-68s %4.2f %s" %
1491 (self.getDescription(test),
1492 time.time() - self.start_test,
1493 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001494
1495 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001496
Damjan Marionf56b77a2016-10-03 19:44:57 +02001497 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001498 """
1499 Print errors from running the test case
1500 """
juraj.linkesabec0122018-11-16 17:28:56 +01001501 if len(self.errors) > 0 or len(self.failures) > 0:
1502 self.stream.writeln()
1503 self.printErrorList('ERROR', self.errors)
1504 self.printErrorList('FAIL', self.failures)
1505
1506 # ^^ that is the last output from unittest before summary
1507 if not self.runner.print_summary:
1508 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1509 self.stream = devnull
1510 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001511
Damjan Marionf56b77a2016-10-03 19:44:57 +02001512 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001513 """
1514 Print error list to the output stream together with error type
1515 and test case description.
1516
1517 :param flavour: error type
1518 :param errors: iterable errors
1519
1520 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001521 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001522 self.stream.writeln(double_line_delim)
1523 self.stream.writeln("%s: %s" %
1524 (flavour, self.getDescription(test)))
1525 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001526 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001527
1528
Damjan Marionf56b77a2016-10-03 19:44:57 +02001529class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001530 """
Klement Sekera104543f2017-02-03 07:29:43 +01001531 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001532 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001533
Klement Sekeraf62ae122016-10-11 11:47:09 +02001534 @property
1535 def resultclass(self):
1536 """Class maintaining the results of the tests"""
1537 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001538
juraj.linkes184870a2018-07-16 14:22:01 +02001539 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001540 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001541 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001542 # ignore stream setting here, use hard-coded stdout to be in sync
1543 # with prints from VppTestCase methods ...
1544 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1545 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001546 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001547 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001548
juraj.linkesabec0122018-11-16 17:28:56 +01001549 self.orig_stream = self.stream
1550 self.resultclass.test_framework_result_pipe = result_pipe
1551
1552 self.print_summary = print_summary
1553
1554 def _makeResult(self):
1555 return self.resultclass(self.stream,
1556 self.descriptions,
1557 self.verbosity,
1558 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001559
Damjan Marionf56b77a2016-10-03 19:44:57 +02001560 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001561 """
1562 Run the tests
1563
1564 :param test:
1565
1566 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001567 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001568
1569 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001570 if not self.print_summary:
1571 self.stream = self.orig_stream
1572 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001573 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001574
1575
1576class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001577 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1578 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001579 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001580 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001581 if hasattr(self, 'testcase') and self.testcase.debug_all:
1582 if self.testcase.debug_gdbserver:
1583 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1584 .format(port=self.testcase.gdbserver_port)] + args
1585 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1586 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001587 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001588 self.app_name = os.path.basename(self.app_bin)
1589 if hasattr(self, 'role'):
1590 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001591 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001592 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001593 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001594 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001595
Dave Wallace24564332019-10-21 02:53:14 +00001596 def wait_for_enter(self):
1597 if not hasattr(self, 'testcase'):
1598 return
1599 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1600 print()
1601 print(double_line_delim)
1602 print("Spawned GDB Server for '{app}' with PID: {pid}"
1603 .format(app=self.app_name, pid=self.process.pid))
1604 elif self.testcase.debug_all and self.testcase.debug_gdb:
1605 print()
1606 print(double_line_delim)
1607 print("Spawned '{app}' with PID: {pid}"
1608 .format(app=self.app_name, pid=self.process.pid))
1609 else:
1610 return
1611 print(single_line_delim)
1612 print("You can debug '{app}' using:".format(app=self.app_name))
1613 if self.testcase.debug_gdbserver:
1614 print("sudo gdb " + self.app_bin +
1615 " -ex 'target remote localhost:{port}'"
1616 .format(port=self.testcase.gdbserver_port))
1617 print("Now is the time to attach gdb by running the above "
1618 "command, set up breakpoints etc., then resume from "
1619 "within gdb by issuing the 'continue' command")
1620 self.testcase.gdbserver_port += 1
1621 elif self.testcase.debug_gdb:
1622 print("sudo gdb " + self.app_bin +
1623 " -ex 'attach {pid}'".format(pid=self.process.pid))
1624 print("Now is the time to attach gdb by running the above "
1625 "command and set up breakpoints etc., then resume from"
1626 " within gdb by issuing the 'continue' command")
1627 print(single_line_delim)
1628 input("Press ENTER to continue running the testcase...")
1629
Neale Ranns812ed392017-10-16 04:20:13 -07001630 def run(self):
1631 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001632 if not os.path.exists(executable) or not os.access(
1633 executable, os.F_OK | os.X_OK):
1634 # Exit code that means some system file did not exist,
1635 # could not be opened, or had some other kind of error.
1636 self.result = os.EX_OSFILE
1637 raise EnvironmentError(
1638 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001639 self.logger.debug("Running executable: '{app}'"
1640 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001641 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001642 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001643 env["CK_LOG_FILE_NAME"] = "-"
1644 self.process = subprocess.Popen(
1645 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1646 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001647 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001648 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001649 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001650 self.logger.info("Return code is `%s'" % self.process.returncode)
1651 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001652 self.logger.info("Executable `{app}' wrote to stdout:"
1653 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001654 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001655 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001656 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001657 self.logger.info("Executable `{app}' wrote to stderr:"
1658 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001659 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001660 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001661 self.logger.info(single_line_delim)
1662 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001663
Klement Sekera6aa58b72019-05-16 14:34:55 +02001664
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001665if __name__ == '__main__':
1666 pass