blob: f3c74ceec3ef09ad84383260f2361f5d6d01d8f6 [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
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 Sekera8d815022021-03-15 16:58:10 +0100409 if not hasattr(cls, "vpp_worker_count"):
410 cls.vpp_worker_count = 0
411 worker_config = os.getenv("VPP_WORKER_CONFIG", "")
412 if worker_config:
413 elems = worker_config.split(" ")
414 if elems[0] != "workers" or len(elems) != 2:
415 raise ValueError("Wrong VPP_WORKER_CONFIG == '%s' value." %
416 worker_config)
417 cls.vpp_worker_count = int(elems[1])
418 if cls.vpp_worker_count > 0 and\
419 cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
420 cls.vpp_worker_count = 0
juraj.linkes184870a2018-07-16 14:22:01 +0200421
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000422 default_variant = os.getenv("VARIANT")
423 if default_variant is not None:
424 default_variant = "defaults { %s 100 }" % default_variant
425 else:
426 default_variant = ""
427
Dave Barach77841402020-04-29 17:04:10 -0400428 api_fuzzing = os.getenv("API_FUZZ")
429 if api_fuzzing is None:
430 api_fuzzing = 'off'
431
Klement Sekera8d815022021-03-15 16:58:10 +0100432 cls.vpp_cmdline = [
433 cls.vpp_bin,
434 "unix", "{", "nodaemon", debug_cli, "full-coredump",
435 coredump_size, "runtime-dir", cls.tempdir, "}",
436 "api-trace", "{", "on", "}",
437 "api-segment", "{", "prefix", cls.shm_prefix, "}",
438 "cpu", "{", "main-core", str(cpu_core_number), ]
439 if cls.vpp_worker_count:
440 cls.vpp_cmdline.extend(["workers", str(cls.vpp_worker_count)])
441 cls.vpp_cmdline.extend([
442 "}",
443 "physmem", "{", "max-size", "32m", "}",
444 "statseg", "{", "socket-name", cls.stats_sock, "}",
445 "socksvr", "{", "socket-name", cls.api_sock, "}",
446 "node { ", default_variant, "}",
447 "api-fuzz {", api_fuzzing, "}",
448 "plugins", "{", "plugin", "dpdk_plugin.so", "{", "disable", "}",
449 "plugin", "rdma_plugin.so", "{", "disable", "}",
450 "plugin", "lisp_unittest_plugin.so", "{", "enable", "}",
451 "plugin", "unittest_plugin.so", "{", "enable", "}"
452 ] + cls.extra_vpp_plugin_config + ["}", ])
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000453
Ole Troana45dc072018-12-21 16:04:22 +0100454 if cls.extra_vpp_punt_config is not None:
455 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100456 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100457 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400458 if cls.test_plugin_path is not None:
459 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
460
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100461 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
462 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200463
464 @classmethod
465 def wait_for_enter(cls):
466 if cls.debug_gdbserver:
467 print(double_line_delim)
468 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
469 elif cls.debug_gdb:
470 print(double_line_delim)
471 print("Spawned VPP with PID: %d" % cls.vpp.pid)
472 else:
473 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
474 return
475 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000476 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200477 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400478 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000479 " -ex 'target remote localhost:{port}'"
480 .format(port=cls.gdbserver_port))
481 print("Now is the time to attach gdb by running the above "
482 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200483 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000484 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200485 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400486 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000487 print("Now is the time to attach gdb by running the above "
488 "command and set up breakpoints etc., then resume VPP from"
489 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200490 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800491 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200492
493 @classmethod
494 def run_vpp(cls):
495 cmdline = cls.vpp_cmdline
496
497 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100498 gdbserver = '/usr/bin/gdbserver'
499 if not os.path.isfile(gdbserver) or \
500 not os.access(gdbserver, os.X_OK):
501 raise Exception("gdbserver binary '%s' does not exist or is "
502 "not executable" % gdbserver)
503
Dave Wallace24564332019-10-21 02:53:14 +0000504 cmdline = [gdbserver, 'localhost:{port}'
505 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200506 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
507
Klement Sekera931be3a2016-11-03 05:36:01 +0100508 try:
509 cls.vpp = subprocess.Popen(cmdline,
510 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100511 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800512 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800513 cls.logger.critical("Subprocess returned with non-0 return code: ("
514 "%s)", e.returncode)
515 raise
516 except OSError as e:
517 cls.logger.critical("Subprocess returned with OS error: "
518 "(%s) %s", e.errno, e.strerror)
519 raise
520 except Exception as e:
521 cls.logger.exception("Subprocess returned unexpected from "
522 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100523 raise
524
Klement Sekera277b89c2016-10-28 13:20:27 +0200525 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100526
Damjan Marionf56b77a2016-10-03 19:44:57 +0200527 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000528 def wait_for_coredump(cls):
529 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400530 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000531 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400532 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000533 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400534 ok = False
535 while time.time() < deadline:
536 cls.sleep(1)
537 size = curr_size
538 curr_size = os.path.getsize(corefile)
539 if size == curr_size:
540 ok = True
541 break
542 if not ok:
543 cls.logger.error("Timed out waiting for coredump to complete:"
544 " %s", corefile)
545 else:
546 cls.logger.error("Coredump complete: %s, size %d",
547 corefile, curr_size)
548
549 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200550 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200551 """
552 Perform class setup before running the testcase
553 Remove shared memory files, start vpp and connect the vpp-api
554 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800555 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100556 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100557 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000558 seed = os.environ["RND_SEED"]
559 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100560 if hasattr(cls, 'parallel_handler'):
561 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100562 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700563
Klement Sekeraf62ae122016-10-11 11:47:09 +0200564 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200565 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200566 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200567 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200568 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
569 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100570 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
571 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200572 cls.file_handler.setLevel(DEBUG)
573 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700574 cls.logger.debug("--- setUpClass() for %s called ---" %
575 cls.__name__)
Ole Troan4376ab22021-03-03 10:40:05 +0100576 cls.shm_prefix = os.path.basename(cls.tempdir) # Only used for VAPI
Klement Sekeraf62ae122016-10-11 11:47:09 +0200577 os.chdir(cls.tempdir)
Ole Troan4376ab22021-03-03 10:40:05 +0100578 cls.logger.info("Temporary dir is %s, api socket is %s",
579 cls.tempdir, cls.api_sock)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000580 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200581 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100582 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100583 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200584 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100585 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100586 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200587 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200588 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200589 # need to catch exceptions here because if we raise, then the cleanup
590 # doesn't get called and we might end with a zombie vpp
591 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200592 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200593 cls.reporter.send_keep_alive(cls, 'setUpClass')
594 VppTestResult.current_test_case_info = TestCaseInfo(
595 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100596 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100597 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100598 cls.pump_thread_stop_flag = Event()
599 cls.pump_thread_wakeup_pipe = os.pipe()
600 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100601 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100602 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200603 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400604 cls.vapi_response_timeout = 0
Ole Troan4376ab22021-03-03 10:40:05 +0100605 cls.vapi = VppPapiProvider(cls.__name__, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400606 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100607 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400608 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100609 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400610 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100611 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200612 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200613 try:
614 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100615 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200616 cls.vpp_startup_failed = True
617 cls.logger.critical(
618 "VPP died shortly after startup, check the"
619 " output to standard error for possible cause")
620 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100621 try:
622 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100623 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500624 cls.logger.debug("Exception connecting to vapi: %s" % e)
625 cls.vapi.disconnect()
626
Klement Sekera085f5c02016-11-24 01:59:16 +0100627 if cls.debug_gdbserver:
628 print(colorize("You're running VPP inside gdbserver but "
629 "VPP-API connection failed, did you forget "
630 "to 'continue' VPP from within gdb?", RED))
Ole Troan4376ab22021-03-03 10:40:05 +0100631 raise e
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400632 except vpp_papi.VPPRuntimeError as e:
633 cls.logger.debug("%s" % e)
634 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100635 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000636 except Exception as e:
637 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400638 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100639 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200640
Damjan Marionf56b77a2016-10-03 19:44:57 +0200641 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500642 def _debug_quit(cls):
643 if (cls.debug_gdbserver or cls.debug_gdb):
644 try:
645 cls.vpp.poll()
646
647 if cls.vpp.returncode is None:
648 print()
649 print(double_line_delim)
650 print("VPP or GDB server is still running")
651 print(single_line_delim)
652 input("When done debugging, press ENTER to kill the "
653 "process and finish running the testcase...")
654 except AttributeError:
655 pass
656
657 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200658 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200659 """
660 Disconnect vpp-api, kill vpp and cleanup shared memory files
661 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500662 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200663
juraj.linkes184870a2018-07-16 14:22:01 +0200664 # first signal that we want to stop the pump thread, then wake it up
665 if hasattr(cls, 'pump_thread_stop_flag'):
666 cls.pump_thread_stop_flag.set()
667 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100668 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100669 if hasattr(cls, 'pump_thread'):
670 cls.logger.debug("Waiting for pump thread to stop")
671 cls.pump_thread.join()
672 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500673 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100674 cls.vpp_stderr_reader_thread.join()
675
Klement Sekeraf62ae122016-10-11 11:47:09 +0200676 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100677 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100678 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700679 cls.logger.debug("Disconnecting class vapi client on %s",
680 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100681 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700682 cls.logger.debug("Deleting class vapi attribute on %s",
683 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100684 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200685 cls.vpp.poll()
686 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000687 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100688 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400689 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100690 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100691 try:
692 outs, errs = cls.vpp.communicate(timeout=5)
693 except subprocess.TimeoutExpired:
694 cls.vpp.kill()
695 outs, errs = cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700696 cls.logger.debug("Deleting class vpp attribute on %s",
697 cls.__name__)
Paul Vinciguerrad1f05f72020-12-16 21:03:16 -0500698 cls.vpp.stdout.close()
699 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200700 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200701
Klement Sekera3747c752017-04-10 06:30:17 +0200702 if cls.vpp_startup_failed:
703 stdout_log = cls.logger.info
704 stderr_log = cls.logger.critical
705 else:
706 stdout_log = cls.logger.info
707 stderr_log = cls.logger.info
708
Klement Sekerae4504c62016-12-08 10:16:41 +0100709 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200710 stdout_log(single_line_delim)
711 stdout_log('VPP output to stdout while running %s:', cls.__name__)
712 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100713 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200714 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
715 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200716 stdout_log('\n%s', vpp_output)
717 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200718
Klement Sekerae4504c62016-12-08 10:16:41 +0100719 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200720 stderr_log(single_line_delim)
721 stderr_log('VPP output to stderr while running %s:', cls.__name__)
722 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100723 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200724 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
725 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200726 stderr_log('\n%s', vpp_output)
727 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200728
Damjan Marionf56b77a2016-10-03 19:44:57 +0200729 @classmethod
730 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200731 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700732 cls.logger.debug("--- tearDownClass() for %s called ---" %
733 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200734 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200735 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200736 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100737 cls.reset_packet_infos()
738 if debug_framework:
739 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200740
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700741 def show_commands_at_teardown(self):
742 """ Allow subclass specific teardown logging additions."""
743 self.logger.info("--- No test specific show commands provided. ---")
744
Damjan Marionf56b77a2016-10-03 19:44:57 +0200745 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200746 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100747 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
748 (self.__class__.__name__, self._testMethodName,
749 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700750
751 try:
752 if not self.vpp_dead:
753 self.logger.debug(self.vapi.cli("show trace max 1000"))
754 self.logger.info(self.vapi.ppcli("show interface"))
755 self.logger.info(self.vapi.ppcli("show hardware"))
756 self.logger.info(self.statistics.set_errors_str())
757 self.logger.info(self.vapi.ppcli("show run"))
758 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400759 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700760 self.logger.info("Logging testcase specific show commands.")
761 self.show_commands_at_teardown()
762 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500763 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000764 m = self._testMethodName
765 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500766 tmp_api_trace = "/tmp/%s" % api_trace
767 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
768 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
769 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
770 vpp_api_trace_log))
771 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500772 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500773 vpp_api_trace_log))
Ole Troan4376ab22021-03-03 10:40:05 +0100774 except VppTransportSocketIOError:
775 self.logger.debug("VppTransportSocketIOError: Vpp dead. "
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700776 "Cannot log show commands.")
777 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100778 else:
779 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200780
Damjan Marionf56b77a2016-10-03 19:44:57 +0200781 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200782 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800783 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200784 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100785 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400786 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
787 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100788 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100789 self.vpp_stdout_deque.append(
790 "--- test setUp() for %s.%s(%s) starts here ---\n" %
791 (self.__class__.__name__, self._testMethodName,
792 self._testMethodDoc))
793 self.vpp_stderr_deque.append(
794 "--- test setUp() for %s.%s(%s) starts here ---\n" %
795 (self.__class__.__name__, self._testMethodName,
796 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200797 self.vapi.cli("clear trace")
798 # store the test instance inside the test class - so that objects
799 # holding the class can access instance methods (like assertEqual)
800 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200801
Damjan Marionf56b77a2016-10-03 19:44:57 +0200802 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200803 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200804 """
805 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200806
Klement Sekera75e7d132017-09-20 08:26:30 +0200807 :param interfaces: iterable interface indexes (if None,
808 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200809
Klement Sekeraf62ae122016-10-11 11:47:09 +0200810 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200811 if interfaces is None:
812 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200813 for i in interfaces:
814 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200815
Damjan Marionf56b77a2016-10-03 19:44:57 +0200816 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100817 def register_capture(cls, cap_name):
818 """ Register a capture in the testclass """
819 # add to the list of captures with current timestamp
820 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100821
822 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000823 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400824 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
825 # returns float("2.190522")
826 timestr = cls.vapi.cli('show clock')
827 head, sep, tail = timestr.partition(',')
828 head, sep, tail = head.partition('Time now')
829 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000830
831 @classmethod
832 def sleep_on_vpp_time(cls, sec):
833 """ Sleep according to time in VPP world """
834 # On a busy system with many processes
835 # we might end up with VPP time being slower than real world
836 # So take that into account when waiting for VPP to do something
837 start_time = cls.get_vpp_time()
838 while cls.get_vpp_time() - start_time < sec:
839 cls.sleep(0.1)
840
841 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100842 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000843 """ Enable the PG, wait till it is done, then clean up """
Benoît Ganne8c45e512021-02-19 16:39:13 +0100844 if trace:
845 cls.vapi.cli("clear trace")
846 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200847 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000848 # PG, when starts, runs to completion -
849 # so let's avoid a race condition,
850 # and wait a little till it's done.
851 # Then clean it up - and then be gone.
852 deadline = time.time() + 300
853 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
854 cls.sleep(0.01) # yield
855 if time.time() > deadline:
856 cls.logger.error("Timeout waiting for pg to stop")
857 break
858 for stamp, cap_name in cls._captures:
859 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100860 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200861
Damjan Marionf56b77a2016-10-03 19:44:57 +0200862 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200863 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200864 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100865 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200866
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100867 :param interfaces: iterable indexes of the interfaces.
868 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200869
Klement Sekeraf62ae122016-10-11 11:47:09 +0200870 """
871 result = []
872 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200873 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200874 setattr(cls, intf.name, intf)
875 result.append(intf)
876 cls.pg_interfaces = result
877 return result
878
Matej Klotton0178d522016-11-04 11:11:44 +0100879 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200880 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100881 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100882 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100883
Klement Sekerab9ef2732018-06-24 22:49:33 +0200884 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100885 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100886 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200887 result = [VppLoInterface(cls) for i in range(count)]
888 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100889 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100890 cls.lo_interfaces = result
891 return result
892
Neale Ranns192b13f2019-03-15 02:16:20 -0700893 @classmethod
894 def create_bvi_interfaces(cls, count):
895 """
896 Create BVI interfaces.
897
898 :param count: number of interfaces created.
899 :returns: List of created interfaces.
900 """
901 result = [VppBviInterface(cls) for i in range(count)]
902 for intf in result:
903 setattr(cls, intf.name, intf)
904 cls.bvi_interfaces = result
905 return result
906
Damjan Marionf56b77a2016-10-03 19:44:57 +0200907 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200908 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200909 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200910 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200911 NOTE: Currently works only when Raw layer is present.
912
913 :param packet: packet
914 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200915 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200916
917 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200918 packet_len = len(packet) + 4
919 extend = size - packet_len
920 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200921 num = (extend // len(padding)) + 1
922 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200923
Klement Sekeradab231a2016-12-21 08:50:14 +0100924 @classmethod
925 def reset_packet_infos(cls):
926 """ Reset the list of packet info objects and packet counts to zero """
927 cls._packet_infos = {}
928 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200929
Klement Sekeradab231a2016-12-21 08:50:14 +0100930 @classmethod
931 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200932 """
933 Create packet info object containing the source and destination indexes
934 and add it to the testcase's packet info list
935
Klement Sekeradab231a2016-12-21 08:50:14 +0100936 :param VppInterface src_if: source interface
937 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200938
939 :returns: _PacketInfo object
940
941 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200942 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100943 info.index = len(cls._packet_infos)
944 info.src = src_if.sw_if_index
945 info.dst = dst_if.sw_if_index
946 if isinstance(dst_if, VppSubInterface):
947 dst_idx = dst_if.parent.sw_if_index
948 else:
949 dst_idx = dst_if.sw_if_index
950 if dst_idx in cls._packet_count_for_dst_if_idx:
951 cls._packet_count_for_dst_if_idx[dst_idx] += 1
952 else:
953 cls._packet_count_for_dst_if_idx[dst_idx] = 1
954 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200955 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200956
Damjan Marionf56b77a2016-10-03 19:44:57 +0200957 @staticmethod
958 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200959 """
960 Convert _PacketInfo object to packet payload
961
962 :param info: _PacketInfo object
963
964 :returns: string containing serialized data from packet info
965 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100966 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
967 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200968
Damjan Marionf56b77a2016-10-03 19:44:57 +0200969 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800970 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200971 """
972 Convert packet payload to _PacketInfo object
973
974 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700975 :type payload: <class 'scapy.packet.Raw'>
976 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800977 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700978 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200979 :returns: _PacketInfo object containing de-serialized data from payload
980
981 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800982 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200983 info = _PacketInfo()
984 info.index = int(numbers[0])
985 info.src = int(numbers[1])
986 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100987 info.ip = int(numbers[3])
988 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200989 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200990
Damjan Marionf56b77a2016-10-03 19:44:57 +0200991 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200992 """
993 Iterate over the packet info list stored in the testcase
994 Start iteration with first element if info is None
995 Continue based on index in info if info is specified
996
997 :param info: info or None
998 :returns: next info in list or None if no more infos
999 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001000 if info is None:
1001 next_index = 0
1002 else:
1003 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001004 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001005 return None
1006 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001007 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001008
Klement Sekeraf62ae122016-10-11 11:47:09 +02001009 def get_next_packet_info_for_interface(self, src_index, info):
1010 """
1011 Search the packet info list for the next packet info with same source
1012 interface index
1013
1014 :param src_index: source interface index to search for
1015 :param info: packet info - where to start the search
1016 :returns: packet info or None
1017
1018 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001019 while True:
1020 info = self.get_next_packet_info(info)
1021 if info is None:
1022 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001023 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001024 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001025
Klement Sekeraf62ae122016-10-11 11:47:09 +02001026 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1027 """
1028 Search the packet info list for the next packet info with same source
1029 and destination interface indexes
1030
1031 :param src_index: source interface index to search for
1032 :param dst_index: destination interface index to search for
1033 :param info: packet info - where to start the search
1034 :returns: packet info or None
1035
1036 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001037 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001038 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001039 if info is None:
1040 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001041 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001042 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001043
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001044 def assert_equal(self, real_value, expected_value, name_or_class=None):
1045 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001046 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001047 return
1048 try:
1049 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1050 msg = msg % (getdoc(name_or_class).strip(),
1051 real_value, str(name_or_class(real_value)),
1052 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001053 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001054 msg = "Invalid %s: %s does not match expected value %s" % (
1055 name_or_class, real_value, expected_value)
1056
1057 self.assertEqual(real_value, expected_value, msg)
1058
Klement Sekerab17dd962017-01-09 07:43:48 +01001059 def assert_in_range(self,
1060 real_value,
1061 expected_min,
1062 expected_max,
1063 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001064 if name is None:
1065 msg = None
1066 else:
1067 msg = "Invalid %s: %s out of range <%s,%s>" % (
1068 name, real_value, expected_min, expected_max)
1069 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1070
Klement Sekerad81ae412018-05-16 10:52:54 +02001071 def assert_packet_checksums_valid(self, packet,
1072 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001073 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001074 udp_layers = ['UDP', 'UDPerror']
1075 checksum_fields = ['cksum', 'chksum']
1076 checksums = []
1077 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001078 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001079 while True:
1080 layer = temp.getlayer(counter)
1081 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001082 layer = layer.copy()
1083 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001084 for cf in checksum_fields:
1085 if hasattr(layer, cf):
1086 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001087 0 == getattr(layer, cf) and \
1088 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001089 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001090 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001091 checksums.append((counter, cf))
1092 else:
1093 break
1094 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001095 if 0 == len(checksums):
1096 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001097 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001098 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001099 calc_sum = getattr(temp[layer], cf)
1100 self.assert_equal(
1101 getattr(received[layer], cf), calc_sum,
1102 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1103 self.logger.debug(
1104 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1105 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001106
1107 def assert_checksum_valid(self, received_packet, layer,
1108 field_name='chksum',
1109 ignore_zero_checksum=False):
1110 """ Check checksum of received packet on given layer """
1111 received_packet_checksum = getattr(received_packet[layer], field_name)
1112 if ignore_zero_checksum and 0 == received_packet_checksum:
1113 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001114 recalculated = received_packet.__class__(
1115 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001116 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001117 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001118 self.assert_equal(received_packet_checksum,
1119 getattr(recalculated[layer], field_name),
1120 "packet checksum on layer: %s" % layer)
1121
1122 def assert_ip_checksum_valid(self, received_packet,
1123 ignore_zero_checksum=False):
1124 self.assert_checksum_valid(received_packet, 'IP',
1125 ignore_zero_checksum=ignore_zero_checksum)
1126
1127 def assert_tcp_checksum_valid(self, received_packet,
1128 ignore_zero_checksum=False):
1129 self.assert_checksum_valid(received_packet, 'TCP',
1130 ignore_zero_checksum=ignore_zero_checksum)
1131
1132 def assert_udp_checksum_valid(self, received_packet,
1133 ignore_zero_checksum=True):
1134 self.assert_checksum_valid(received_packet, 'UDP',
1135 ignore_zero_checksum=ignore_zero_checksum)
1136
1137 def assert_embedded_icmp_checksum_valid(self, received_packet):
1138 if received_packet.haslayer(IPerror):
1139 self.assert_checksum_valid(received_packet, 'IPerror')
1140 if received_packet.haslayer(TCPerror):
1141 self.assert_checksum_valid(received_packet, 'TCPerror')
1142 if received_packet.haslayer(UDPerror):
1143 self.assert_checksum_valid(received_packet, 'UDPerror',
1144 ignore_zero_checksum=True)
1145 if received_packet.haslayer(ICMPerror):
1146 self.assert_checksum_valid(received_packet, 'ICMPerror')
1147
1148 def assert_icmp_checksum_valid(self, received_packet):
1149 self.assert_checksum_valid(received_packet, 'ICMP')
1150 self.assert_embedded_icmp_checksum_valid(received_packet)
1151
1152 def assert_icmpv6_checksum_valid(self, pkt):
1153 if pkt.haslayer(ICMPv6DestUnreach):
1154 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1155 self.assert_embedded_icmp_checksum_valid(pkt)
1156 if pkt.haslayer(ICMPv6EchoRequest):
1157 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1158 if pkt.haslayer(ICMPv6EchoReply):
1159 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1160
Klement Sekera3a343d42019-05-16 14:35:46 +02001161 def get_packet_counter(self, counter):
1162 if counter.startswith("/"):
1163 counter_value = self.statistics.get_counter(counter)
1164 else:
1165 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001166 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001167 for i in range(1, len(counters) - 1):
1168 results = counters[i].split()
1169 if results[1] == counter:
1170 counter_value = int(results[0])
1171 break
1172 return counter_value
1173
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001174 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001175 counter_value = self.get_packet_counter(counter)
1176 self.assert_equal(counter_value, expected_value,
1177 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001178
Ole Troan233e4682019-05-16 15:01:34 +02001179 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001180 counter_value = self.statistics[counter].sum()
Ole Troan233e4682019-05-16 15:01:34 +02001181 self.assert_equal(counter_value, expected_value,
1182 "error counter `%s'" % counter)
1183
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001184 @classmethod
1185 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001186
1187 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1188 # * by Guido, only the main thread can be interrupted.
1189 # */
1190 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1191 if timeout == 0:
1192 # yield quantum
1193 if hasattr(os, 'sched_yield'):
1194 os.sched_yield()
1195 else:
1196 time.sleep(0)
1197 return
1198
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001199 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001200 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001201 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001202 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001203 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001204 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001205 "slept for %es instead of ~%es!",
1206 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001207
1208 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001209 "Finished sleep (%s) - slept %es (wanted %es)",
1210 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001211
Benoît Ganne8c45e512021-02-19 16:39:13 +01001212 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001213 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001214 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001215 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001216
1217 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1218 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001219 if not timeout:
1220 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001221 for i in self.pg_interfaces:
1222 i.get_capture(0, timeout=timeout)
1223 i.assert_nothing_captured(remark=remark)
1224 timeout = 0.1
1225
Benoît Ganne8c45e512021-02-19 16:39:13 +01001226 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1227 trace=True):
Neale Rannsd7603d92019-03-28 08:56:10 +00001228 if not n_rx:
1229 n_rx = len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001230 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001231 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001232 return rx
1233
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001234 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1235 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001236 rx = output.get_capture(len(pkts))
1237 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001238 if not timeout:
1239 timeout = 1
1240 for i in self.pg_interfaces:
1241 if i not in outputs:
1242 i.get_capture(0, timeout=timeout)
1243 i.assert_nothing_captured()
1244 timeout = 0.1
1245
Neale Ranns52fae862018-01-08 04:41:42 -08001246 return rx
1247
Damjan Marionf56b77a2016-10-03 19:44:57 +02001248
juraj.linkes184870a2018-07-16 14:22:01 +02001249def get_testcase_doc_name(test):
1250 return getdoc(test.__class__).splitlines()[0]
1251
1252
Ole Trøan5ba91592018-11-22 10:01:09 +00001253def get_test_description(descriptions, test):
1254 short_description = test.shortDescription()
1255 if descriptions and short_description:
1256 return short_description
1257 else:
1258 return str(test)
1259
1260
juraj.linkes40dd73b2018-09-21 13:55:16 +02001261class TestCaseInfo(object):
1262 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1263 self.logger = logger
1264 self.tempdir = tempdir
1265 self.vpp_pid = vpp_pid
1266 self.vpp_bin_path = vpp_bin_path
1267 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001268
1269
Damjan Marionf56b77a2016-10-03 19:44:57 +02001270class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001271 """
1272 @property result_string
1273 String variable to store the test case result string.
1274 @property errors
1275 List variable containing 2-tuples of TestCase instances and strings
1276 holding formatted tracebacks. Each tuple represents a test which
1277 raised an unexpected exception.
1278 @property failures
1279 List variable containing 2-tuples of TestCase instances and strings
1280 holding formatted tracebacks. Each tuple represents a test where
1281 a failure was explicitly signalled using the TestCase.assert*()
1282 methods.
1283 """
1284
juraj.linkes40dd73b2018-09-21 13:55:16 +02001285 failed_test_cases_info = set()
1286 core_crash_test_cases_info = set()
1287 current_test_case_info = None
1288
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001289 def __init__(self, stream=None, descriptions=None, verbosity=None,
1290 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001291 """
Klement Sekerada505f62017-01-04 12:58:53 +01001292 :param stream File descriptor to store where to report test results.
1293 Set to the standard error stream by default.
1294 :param descriptions Boolean variable to store information if to use
1295 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001296 :param verbosity Integer variable to store required verbosity level.
1297 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001298 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001299 self.stream = stream
1300 self.descriptions = descriptions
1301 self.verbosity = verbosity
1302 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001303 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001304
Damjan Marionf56b77a2016-10-03 19:44:57 +02001305 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001306 """
1307 Record a test succeeded result
1308
1309 :param test:
1310
1311 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001312 if self.current_test_case_info:
1313 self.current_test_case_info.logger.debug(
1314 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1315 test._testMethodName,
1316 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001317 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001318 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001319
juraj.linkescae64f82018-09-19 15:01:47 +02001320 self.send_result_through_pipe(test, PASS)
1321
Klement Sekeraf62ae122016-10-11 11:47:09 +02001322 def addSkip(self, test, reason):
1323 """
1324 Record a test skipped.
1325
1326 :param test:
1327 :param reason:
1328
1329 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001330 if self.current_test_case_info:
1331 self.current_test_case_info.logger.debug(
1332 "--- addSkip() %s.%s(%s) called, reason is %s" %
1333 (test.__class__.__name__, test._testMethodName,
1334 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001335 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001336 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001337
juraj.linkescae64f82018-09-19 15:01:47 +02001338 self.send_result_through_pipe(test, SKIP)
1339
juraj.linkes40dd73b2018-09-21 13:55:16 +02001340 def symlink_failed(self):
1341 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001342 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001343 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001344 link_path = os.path.join(
1345 failed_dir,
1346 '%s-FAILED' %
1347 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001348
1349 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001350 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001351 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001352 "os.symlink(%s, %s)" %
1353 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001354 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001355 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001356 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001357 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001358 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001359
Klement Sekeraf413bef2017-08-15 07:09:02 +02001360 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001361 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001362
juraj.linkescae64f82018-09-19 15:01:47 +02001363 def send_result_through_pipe(self, test, result):
1364 if hasattr(self, 'test_framework_result_pipe'):
1365 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001366 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001367 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001368
juraj.linkes40dd73b2018-09-21 13:55:16 +02001369 def log_error(self, test, err, fn_name):
1370 if self.current_test_case_info:
1371 if isinstance(test, unittest.suite._ErrorHolder):
1372 test_name = test.description
1373 else:
1374 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1375 test._testMethodName,
1376 test._testMethodDoc)
1377 self.current_test_case_info.logger.debug(
1378 "--- %s() %s called, err is %s" %
1379 (fn_name, test_name, err))
1380 self.current_test_case_info.logger.debug(
1381 "formatted exception is:\n%s" %
1382 "".join(format_exception(*err)))
1383
1384 def add_error(self, test, err, unittest_fn, error_type):
1385 if error_type == FAIL:
1386 self.log_error(test, err, 'addFailure')
1387 error_type_str = colorize("FAIL", RED)
1388 elif error_type == ERROR:
1389 self.log_error(test, err, 'addError')
1390 error_type_str = colorize("ERROR", RED)
1391 else:
1392 raise Exception('Error type %s cannot be used to record an '
1393 'error or a failure' % error_type)
1394
1395 unittest_fn(self, test, err)
1396 if self.current_test_case_info:
1397 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1398 (error_type_str,
1399 self.current_test_case_info.tempdir)
1400 self.symlink_failed()
1401 self.failed_test_cases_info.add(self.current_test_case_info)
1402 if is_core_present(self.current_test_case_info.tempdir):
1403 if not self.current_test_case_info.core_crash_test:
1404 if isinstance(test, unittest.suite._ErrorHolder):
1405 test_name = str(test)
1406 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001407 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001408 get_testcase_doc_name(test), test.id())
1409 self.current_test_case_info.core_crash_test = test_name
1410 self.core_crash_test_cases_info.add(
1411 self.current_test_case_info)
1412 else:
1413 self.result_string = '%s [no temp dir]' % error_type_str
1414
1415 self.send_result_through_pipe(test, error_type)
1416
Damjan Marionf56b77a2016-10-03 19:44:57 +02001417 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001418 """
1419 Record a test failed result
1420
1421 :param test:
1422 :param err: error message
1423
1424 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001425 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001426
Damjan Marionf56b77a2016-10-03 19:44:57 +02001427 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001428 """
1429 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001430
Klement Sekeraf62ae122016-10-11 11:47:09 +02001431 :param test:
1432 :param err: error message
1433
1434 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001435 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001436
Damjan Marionf56b77a2016-10-03 19:44:57 +02001437 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001438 """
1439 Get test description
1440
1441 :param test:
1442 :returns: test description
1443
1444 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001445 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001446
Damjan Marionf56b77a2016-10-03 19:44:57 +02001447 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001448 """
1449 Start a test
1450
1451 :param test:
1452
1453 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001454
1455 def print_header(test):
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001456 test_doc = getdoc(test)
1457 if not test_doc:
1458 raise Exception("No doc string for test '%s'" % test.id())
1459 test_title = test_doc.splitlines()[0]
1460 test_title_colored = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001461 if test.is_tagged_run_solo():
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001462 # long live PEP-8 and 80 char width limitation...
1463 c = YELLOW
1464 test_title_colored = colorize("SOLO RUN: " + test_title, c)
1465
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001466 # This block may overwrite the colorized title above,
1467 # but we want this to stand out and be fixed
1468 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
1469 c = RED
1470 w = "FIXME with VPP workers: "
1471 test_title_colored = colorize(w + test_title, c)
1472
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001473 if not hasattr(test.__class__, '_header_printed'):
1474 print(double_line_delim)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001475 print(test_title_colored)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001476 print(double_line_delim)
1477 test.__class__._header_printed = True
1478
1479 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001480 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001481 unittest.TestResult.startTest(self, test)
1482 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001483 self.stream.writeln(
1484 "Starting " + self.getDescription(test) + " ...")
1485 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001486
Damjan Marionf56b77a2016-10-03 19:44:57 +02001487 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001488 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001489 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001490
1491 :param test:
1492
1493 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001494 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001495
Damjan Marionf56b77a2016-10-03 19:44:57 +02001496 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001497 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001498 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001499 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001500 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001501 else:
Ole Troan0c629322019-11-28 14:48:44 +01001502 self.stream.writeln("%-68s %4.2f %s" %
1503 (self.getDescription(test),
1504 time.time() - self.start_test,
1505 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001506
1507 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001508
Damjan Marionf56b77a2016-10-03 19:44:57 +02001509 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001510 """
1511 Print errors from running the test case
1512 """
juraj.linkesabec0122018-11-16 17:28:56 +01001513 if len(self.errors) > 0 or len(self.failures) > 0:
1514 self.stream.writeln()
1515 self.printErrorList('ERROR', self.errors)
1516 self.printErrorList('FAIL', self.failures)
1517
1518 # ^^ that is the last output from unittest before summary
1519 if not self.runner.print_summary:
1520 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1521 self.stream = devnull
1522 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001523
Damjan Marionf56b77a2016-10-03 19:44:57 +02001524 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001525 """
1526 Print error list to the output stream together with error type
1527 and test case description.
1528
1529 :param flavour: error type
1530 :param errors: iterable errors
1531
1532 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001533 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001534 self.stream.writeln(double_line_delim)
1535 self.stream.writeln("%s: %s" %
1536 (flavour, self.getDescription(test)))
1537 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001538 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001539
1540
Damjan Marionf56b77a2016-10-03 19:44:57 +02001541class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001542 """
Klement Sekera104543f2017-02-03 07:29:43 +01001543 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001544 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001545
Klement Sekeraf62ae122016-10-11 11:47:09 +02001546 @property
1547 def resultclass(self):
1548 """Class maintaining the results of the tests"""
1549 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001550
juraj.linkes184870a2018-07-16 14:22:01 +02001551 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001552 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001553 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001554 # ignore stream setting here, use hard-coded stdout to be in sync
1555 # with prints from VppTestCase methods ...
1556 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1557 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001558 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001559 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001560
juraj.linkesabec0122018-11-16 17:28:56 +01001561 self.orig_stream = self.stream
1562 self.resultclass.test_framework_result_pipe = result_pipe
1563
1564 self.print_summary = print_summary
1565
1566 def _makeResult(self):
1567 return self.resultclass(self.stream,
1568 self.descriptions,
1569 self.verbosity,
1570 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001571
Damjan Marionf56b77a2016-10-03 19:44:57 +02001572 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001573 """
1574 Run the tests
1575
1576 :param test:
1577
1578 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001579 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001580
1581 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001582 if not self.print_summary:
1583 self.stream = self.orig_stream
1584 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001585 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001586
1587
1588class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001589 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1590 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001591 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001592 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001593 if hasattr(self, 'testcase') and self.testcase.debug_all:
1594 if self.testcase.debug_gdbserver:
1595 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1596 .format(port=self.testcase.gdbserver_port)] + args
1597 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1598 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001599 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001600 self.app_name = os.path.basename(self.app_bin)
1601 if hasattr(self, 'role'):
1602 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001603 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001604 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001605 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001606 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001607
Dave Wallace24564332019-10-21 02:53:14 +00001608 def wait_for_enter(self):
1609 if not hasattr(self, 'testcase'):
1610 return
1611 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1612 print()
1613 print(double_line_delim)
1614 print("Spawned GDB Server for '{app}' with PID: {pid}"
1615 .format(app=self.app_name, pid=self.process.pid))
1616 elif self.testcase.debug_all and self.testcase.debug_gdb:
1617 print()
1618 print(double_line_delim)
1619 print("Spawned '{app}' with PID: {pid}"
1620 .format(app=self.app_name, pid=self.process.pid))
1621 else:
1622 return
1623 print(single_line_delim)
1624 print("You can debug '{app}' using:".format(app=self.app_name))
1625 if self.testcase.debug_gdbserver:
1626 print("sudo gdb " + self.app_bin +
1627 " -ex 'target remote localhost:{port}'"
1628 .format(port=self.testcase.gdbserver_port))
1629 print("Now is the time to attach gdb by running the above "
1630 "command, set up breakpoints etc., then resume from "
1631 "within gdb by issuing the 'continue' command")
1632 self.testcase.gdbserver_port += 1
1633 elif self.testcase.debug_gdb:
1634 print("sudo gdb " + self.app_bin +
1635 " -ex 'attach {pid}'".format(pid=self.process.pid))
1636 print("Now is the time to attach gdb by running the above "
1637 "command and set up breakpoints etc., then resume from"
1638 " within gdb by issuing the 'continue' command")
1639 print(single_line_delim)
1640 input("Press ENTER to continue running the testcase...")
1641
Neale Ranns812ed392017-10-16 04:20:13 -07001642 def run(self):
1643 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001644 if not os.path.exists(executable) or not os.access(
1645 executable, os.F_OK | os.X_OK):
1646 # Exit code that means some system file did not exist,
1647 # could not be opened, or had some other kind of error.
1648 self.result = os.EX_OSFILE
1649 raise EnvironmentError(
1650 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001651 self.logger.debug("Running executable: '{app}'"
1652 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001653 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001654 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001655 env["CK_LOG_FILE_NAME"] = "-"
1656 self.process = subprocess.Popen(
1657 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1658 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001659 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001660 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001661 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001662 self.logger.info("Return code is `%s'" % self.process.returncode)
1663 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001664 self.logger.info("Executable `{app}' wrote to stdout:"
1665 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001666 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001667 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001668 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001669 self.logger.info("Executable `{app}' wrote to stderr:"
1670 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001671 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001672 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001673 self.logger.info(single_line_delim)
1674 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001675
Klement Sekera6aa58b72019-05-16 14:34:55 +02001676
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001677if __name__ == '__main__':
1678 pass