blob: 731c5e18043fcc8b7ec083cb0305af23775c60d6 [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
Klement Sekera558ceab2021-04-08 19:37:41 +020025from abc import ABC, abstractmethod
Ray Kinsellab8165b92021-09-22 11:24:06 +010026from struct import pack, unpack
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070027
28import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000029from scapy.packet import Raw
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040030import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080031from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010032from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000033from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070034from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000035from vpp_papi_provider import VppPapiProvider
Neale Ranns6197cb72021-06-03 14:43:21 +000036from vpp_papi import VppEnum
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050037import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000038from vpp_papi.vpp_stats import VPPStats
Ole Troan4376ab22021-03-03 10:40:05 +010039from vpp_papi.vpp_transport_socket import VppTransportSocketIOError
Ole Trøan162989e2018-11-26 10:27:50 +000040from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
41 get_logger, colorize
42from vpp_object import VppObjectRegistry
43from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020044from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
45from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
46from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080047
Klement Sekera558ceab2021-04-08 19:37:41 +020048from cpu_config import available_cpus, num_cpus, max_vpp_cpus
49
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050050logger = logging.getLogger(__name__)
51
52# Set up an empty logger for the testcase that can be overridden as necessary
53null_logger = logging.getLogger('VppTestCase')
54null_logger.addHandler(logging.NullHandler())
55
juraj.linkescae64f82018-09-19 15:01:47 +020056PASS = 0
57FAIL = 1
58ERROR = 2
59SKIP = 3
60TEST_RUN = 4
Klement Sekera558ceab2021-04-08 19:37:41 +020061SKIP_CPU_SHORTAGE = 5
juraj.linkescae64f82018-09-19 15:01:47 +020062
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040063
64class BoolEnvironmentVariable(object):
65
66 def __init__(self, env_var_name, default='n', true_values=None):
67 self.name = env_var_name
68 self.default = default
69 self.true_values = true_values if true_values is not None else \
70 ("y", "yes", "1")
71
72 def __bool__(self):
73 return os.getenv(self.name, self.default).lower() in self.true_values
74
75 if sys.version_info[0] == 2:
76 __nonzero__ = __bool__
77
78 def __repr__(self):
79 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
80 (self.name, self.default, self.true_values)
81
82
83debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
84if debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010085 import debug_internal
86
Klement Sekeraf62ae122016-10-11 11:47:09 +020087"""
88 Test framework module.
89
90 The module provides a set of tools for constructing and running tests and
91 representing the results.
92"""
93
Klement Sekeraf62ae122016-10-11 11:47:09 +020094
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040095class VppDiedError(Exception):
96 """ exception for reporting that the subprocess has died."""
97
98 signals_by_value = {v: k for k, v in signal.__dict__.items() if
99 k.startswith('SIG') and not k.startswith('SIG_')}
100
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400101 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400102 self.rv = rv
103 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400104 self.testcase = testcase
105 self.method_name = method_name
106
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400107 try:
108 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400109 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400110 pass
111
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400112 if testcase is None and method_name is None:
113 in_msg = ''
114 else:
Klement Sekera79a31db2021-03-12 18:16:10 +0100115 in_msg = ' while running %s.%s' % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400116
Klement Sekera79a31db2021-03-12 18:16:10 +0100117 if self.rv:
118 msg = "VPP subprocess died unexpectedly%s with return code: %d%s."\
119 % (in_msg, self.rv, ' [%s]' %
120 (self.signal_name if
121 self.signal_name is not None else ''))
122 else:
123 msg = "VPP subprocess died unexpectedly%s." % in_msg
124
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400125 super(VppDiedError, self).__init__(msg)
126
127
Damjan Marionf56b77a2016-10-03 19:44:57 +0200128class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200129 """Private class to create packet info object.
130
131 Help process information about the next packet.
132 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200133 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100134 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200135 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100136 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200137 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100138 #: Store the index of the destination packet generator interface
139 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200140 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100141 #: Store expected ip version
142 ip = -1
143 #: Store expected upper protocol
144 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100145 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200146 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200147
Matej Klotton16a14cd2016-12-07 15:09:13 +0100148 def __eq__(self, other):
149 index = self.index == other.index
150 src = self.src == other.src
151 dst = self.dst == other.dst
152 data = self.data == other.data
153 return index and src and dst and data
154
Klement Sekeraf62ae122016-10-11 11:47:09 +0200155
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100156def pump_output(testclass):
157 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100158 stdout_fragment = ""
159 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400160 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100161 readable = select.select([testclass.vpp.stdout.fileno(),
162 testclass.vpp.stderr.fileno(),
163 testclass.pump_thread_wakeup_pipe[0]],
164 [], [])[0]
165 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100166 read = os.read(testclass.vpp.stdout.fileno(), 102400)
167 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200168 split = read.decode('ascii',
169 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100170 if len(stdout_fragment) > 0:
171 split[0] = "%s%s" % (stdout_fragment, split[0])
172 if len(split) > 0 and split[-1].endswith("\n"):
173 limit = None
174 else:
175 limit = -1
176 stdout_fragment = split[-1]
177 testclass.vpp_stdout_deque.extend(split[:limit])
178 if not testclass.cache_vpp_output:
179 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100180 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100181 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100182 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100183 read = os.read(testclass.vpp.stderr.fileno(), 102400)
184 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200185 split = read.decode('ascii',
186 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100187 if len(stderr_fragment) > 0:
188 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200189 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100190 limit = None
191 else:
192 limit = -1
193 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200194
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100195 testclass.vpp_stderr_deque.extend(split[:limit])
196 if not testclass.cache_vpp_output:
197 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100198 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100199 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800200 # ignoring the dummy pipe here intentionally - the
201 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200202
203
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800204def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400205 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100206
Klement Sekera6aa58b72019-05-16 14:34:55 +0200207
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800208is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100209
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800210
211def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100212 return platform.machine() == 'aarch64'
213
Klement Sekera6aa58b72019-05-16 14:34:55 +0200214
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800215is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100216
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800217
218def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400219 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100220
Klement Sekera6aa58b72019-05-16 14:34:55 +0200221
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800222running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100223
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800224
Dave Barachd498c9e2020-03-10 16:59:39 -0400225def _running_gcov_tests():
226 return BoolEnvironmentVariable("GCOV_TESTS")
227
228
229running_gcov_tests = _running_gcov_tests()
230
231
Klement Sekera558ceab2021-04-08 19:37:41 +0200232def get_environ_vpp_worker_count():
233 worker_config = os.getenv("VPP_WORKER_CONFIG", None)
234 if worker_config:
235 elems = worker_config.split(" ")
236 if elems[0] != "workers" or len(elems) != 2:
237 raise ValueError("Wrong VPP_WORKER_CONFIG == '%s' value." %
238 worker_config)
239 return int(elems[1])
240 else:
241 return 0
242
243
244environ_vpp_worker_count = get_environ_vpp_worker_count()
245
246
Klement Sekera909a6a12017-08-08 04:33:53 +0200247class KeepAliveReporter(object):
248 """
249 Singleton object which reports test start to parent process
250 """
251 _shared_state = {}
252
253 def __init__(self):
254 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800255 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200256
257 @property
258 def pipe(self):
259 return self._pipe
260
261 @pipe.setter
262 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800263 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200264 raise Exception("Internal error - pipe should only be set once.")
265 self._pipe = pipe
266
juraj.linkes40dd73b2018-09-21 13:55:16 +0200267 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200268 """
269 Write current test tmpdir & desc to keep-alive pipe to signal liveness
270 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200271 if self.pipe is None:
272 # if not running forked..
273 return
274
Klement Sekera909a6a12017-08-08 04:33:53 +0200275 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200276 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200277 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200278 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200279
Dave Wallacee2efd122017-09-30 22:04:21 -0400280 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200281
282
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000283class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000284 # marks the suites that must run at the end
285 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000286 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000287 # marks the suites broken on VPP multi-worker
288 FIXME_VPP_WORKERS = 2
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000289
290
291def create_tag_decorator(e):
292 def decorator(cls):
293 try:
294 cls.test_tags.append(e)
295 except AttributeError:
296 cls.test_tags = [e]
297 return cls
298 return decorator
299
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000300
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000301tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000302tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000303
304
Klement Sekerae2636852021-03-16 12:52:12 +0100305class DummyVpp:
306 returncode = None
307 pid = 0xcafebafe
308
309 def poll(self):
310 pass
311
312 def terminate(self):
313 pass
314
315
Klement Sekera558ceab2021-04-08 19:37:41 +0200316class CPUInterface(ABC):
317 cpus = []
318 skipped_due_to_cpu_lack = False
319
320 @classmethod
321 @abstractmethod
322 def get_cpus_required(cls):
323 pass
324
325 @classmethod
326 def assign_cpus(cls, cpus):
327 cls.cpus = cpus
328
329
330class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100331 """This subclass is a base class for VPP test cases that are implemented as
332 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200333 """
334
Arthur de Kerhordb023802021-03-11 10:26:54 -0800335 extra_vpp_statseg_config = ""
Ole Troana45dc072018-12-21 16:04:22 +0100336 extra_vpp_punt_config = []
337 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500338 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400339 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100340
Klement Sekeraf62ae122016-10-11 11:47:09 +0200341 @property
342 def packet_infos(self):
343 """List of packet infos"""
344 return self._packet_infos
345
Klement Sekeradab231a2016-12-21 08:50:14 +0100346 @classmethod
347 def get_packet_count_for_if_idx(cls, dst_if_index):
348 """Get the number of packet info for specified destination if index"""
349 if dst_if_index in cls._packet_count_for_dst_if_idx:
350 return cls._packet_count_for_dst_if_idx[dst_if_index]
351 else:
352 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200353
354 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000355 def has_tag(cls, tag):
356 """ if the test case has a given tag - return true """
357 try:
358 return tag in cls.test_tags
359 except AttributeError:
360 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000361 return False
362
363 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000364 def is_tagged_run_solo(cls):
365 """ if the test case class is timing-sensitive - return true """
366 return cls.has_tag(TestCaseTag.RUN_SOLO)
367
368 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200369 def instance(cls):
370 """Return the instance of this testcase"""
371 return cls.test_instance
372
Damjan Marionf56b77a2016-10-03 19:44:57 +0200373 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200374 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000375 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200376 cls.debug_core = False
377 cls.debug_gdb = False
378 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000379 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100380 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200381 if d is None:
382 return
383 dl = d.lower()
384 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200385 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000386 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200387 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000388 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200389 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100390 elif dl == "attach":
391 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200392 else:
393 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000394 if dl == "gdb-all" or dl == "gdbserver-all":
395 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200396
Klement Sekera558ceab2021-04-08 19:37:41 +0200397 @classmethod
398 def get_vpp_worker_count(cls):
399 if not hasattr(cls, "vpp_worker_count"):
400 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
401 cls.vpp_worker_count = 0
402 else:
403 cls.vpp_worker_count = environ_vpp_worker_count
404 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200405
Klement Sekera558ceab2021-04-08 19:37:41 +0200406 @classmethod
407 def get_cpus_required(cls):
408 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200409
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800410 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200411 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200412 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400413 cls.step = BoolEnvironmentVariable('STEP')
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400414 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100415 c = os.getenv("CACHE_OUTPUT", "1")
416 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekerab8c72a42018-11-08 11:21:39 +0100417 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
418 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400419 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100420 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
421 plugin_path = None
422 if cls.plugin_path is not None:
423 if cls.extern_plugin_path is not None:
424 plugin_path = "%s:%s" % (
425 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100426 else:
427 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100428 elif cls.extern_plugin_path is not None:
429 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100430 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100431 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100432 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100433 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100434 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100435 if size is not None:
436 coredump_size = "coredump-size %s" % size
437 if coredump_size is None:
438 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200439
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000440 default_variant = os.getenv("VARIANT")
441 if default_variant is not None:
442 default_variant = "defaults { %s 100 }" % default_variant
443 else:
444 default_variant = ""
445
Dave Barach77841402020-04-29 17:04:10 -0400446 api_fuzzing = os.getenv("API_FUZZ")
447 if api_fuzzing is None:
448 api_fuzzing = 'off'
449
Klement Sekera8d815022021-03-15 16:58:10 +0100450 cls.vpp_cmdline = [
451 cls.vpp_bin,
452 "unix", "{", "nodaemon", debug_cli, "full-coredump",
453 coredump_size, "runtime-dir", cls.tempdir, "}",
454 "api-trace", "{", "on", "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100455 "api-segment", "{", "prefix", cls.get_api_segment_prefix(), "}",
Klement Sekera558ceab2021-04-08 19:37:41 +0200456 "cpu", "{", "main-core", str(cls.cpus[0]), ]
457 if cls.get_vpp_worker_count():
458 cls.vpp_cmdline.extend([
459 "corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])])
Klement Sekera8d815022021-03-15 16:58:10 +0100460 cls.vpp_cmdline.extend([
461 "}",
462 "physmem", "{", "max-size", "32m", "}",
Arthur de Kerhordb023802021-03-11 10:26:54 -0800463 "statseg", "{", "socket-name", cls.get_stats_sock_path(),
464 cls.extra_vpp_statseg_config, "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100465 "socksvr", "{", "socket-name", cls.get_api_sock_path(), "}",
Klement Sekera8d815022021-03-15 16:58:10 +0100466 "node { ", default_variant, "}",
467 "api-fuzz {", api_fuzzing, "}",
468 "plugins", "{", "plugin", "dpdk_plugin.so", "{", "disable", "}",
469 "plugin", "rdma_plugin.so", "{", "disable", "}",
470 "plugin", "lisp_unittest_plugin.so", "{", "enable", "}",
471 "plugin", "unittest_plugin.so", "{", "enable", "}"
472 ] + cls.extra_vpp_plugin_config + ["}", ])
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000473
Ole Troana45dc072018-12-21 16:04:22 +0100474 if cls.extra_vpp_punt_config is not None:
475 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100476 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100477 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400478 if cls.test_plugin_path is not None:
479 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
480
Klement Sekerae2636852021-03-16 12:52:12 +0100481 if not cls.debug_attach:
482 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
483 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200484
485 @classmethod
486 def wait_for_enter(cls):
487 if cls.debug_gdbserver:
488 print(double_line_delim)
489 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
490 elif cls.debug_gdb:
491 print(double_line_delim)
492 print("Spawned VPP with PID: %d" % cls.vpp.pid)
493 else:
494 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
495 return
496 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000497 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200498 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400499 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000500 " -ex 'target remote localhost:{port}'"
501 .format(port=cls.gdbserver_port))
502 print("Now is the time to attach gdb by running the above "
503 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200504 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000505 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200506 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400507 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000508 print("Now is the time to attach gdb by running the above "
509 "command and set up breakpoints etc., then resume VPP from"
510 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200511 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800512 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200513
514 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100515 def attach_vpp(cls):
516 cls.vpp = DummyVpp()
517
518 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200519 def run_vpp(cls):
Klement Sekera558ceab2021-04-08 19:37:41 +0200520 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200521 cmdline = cls.vpp_cmdline
522
523 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100524 gdbserver = '/usr/bin/gdbserver'
Klement Sekera558ceab2021-04-08 19:37:41 +0200525 if not os.path.isfile(gdbserver) or\
Klement Sekera931be3a2016-11-03 05:36:01 +0100526 not os.access(gdbserver, os.X_OK):
527 raise Exception("gdbserver binary '%s' does not exist or is "
528 "not executable" % gdbserver)
529
Dave Wallace24564332019-10-21 02:53:14 +0000530 cmdline = [gdbserver, 'localhost:{port}'
531 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200532 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
533
Klement Sekera931be3a2016-11-03 05:36:01 +0100534 try:
535 cls.vpp = subprocess.Popen(cmdline,
536 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100537 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800538 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800539 cls.logger.critical("Subprocess returned with non-0 return code: ("
540 "%s)", e.returncode)
541 raise
542 except OSError as e:
543 cls.logger.critical("Subprocess returned with OS error: "
544 "(%s) %s", e.errno, e.strerror)
545 raise
546 except Exception as e:
547 cls.logger.exception("Subprocess returned unexpected from "
548 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100549 raise
550
Klement Sekera277b89c2016-10-28 13:20:27 +0200551 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100552
Damjan Marionf56b77a2016-10-03 19:44:57 +0200553 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000554 def wait_for_coredump(cls):
555 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400556 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000557 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400558 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000559 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400560 ok = False
561 while time.time() < deadline:
562 cls.sleep(1)
563 size = curr_size
564 curr_size = os.path.getsize(corefile)
565 if size == curr_size:
566 ok = True
567 break
568 if not ok:
569 cls.logger.error("Timed out waiting for coredump to complete:"
570 " %s", corefile)
571 else:
572 cls.logger.error("Coredump complete: %s, size %d",
573 corefile, curr_size)
574
575 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100576 def get_stats_sock_path(cls):
577 return "%s/stats.sock" % cls.tempdir
578
579 @classmethod
580 def get_api_sock_path(cls):
581 return "%s/api.sock" % cls.tempdir
582
583 @classmethod
584 def get_api_segment_prefix(cls):
585 return os.path.basename(cls.tempdir) # Only used for VAPI
586
587 @classmethod
588 def get_tempdir(cls):
589 if cls.debug_attach:
590 return os.getenv("VPP_IN_GDB_TMP_DIR",
591 "/tmp/unittest-attach-gdb")
592 else:
593 return tempfile.mkdtemp(prefix='vpp-unittest-%s-' % cls.__name__)
594
595 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200596 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200597 """
598 Perform class setup before running the testcase
599 Remove shared memory files, start vpp and connect the vpp-api
600 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800601 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100602 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000603 seed = os.environ["RND_SEED"]
604 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100605 if hasattr(cls, 'parallel_handler'):
606 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100607 cls.logger.propagate = False
Klement Sekerae2636852021-03-16 12:52:12 +0100608 d = os.getenv("DEBUG", None)
609 cls.set_debug_flags(d)
610 cls.tempdir = cls.get_tempdir()
Klement Sekera027dbd52017-04-11 06:01:53 +0200611 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
612 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100613 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
614 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200615 cls.file_handler.setLevel(DEBUG)
616 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100617 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200618 os.chdir(cls.tempdir)
Ole Troan4376ab22021-03-03 10:40:05 +0100619 cls.logger.info("Temporary dir is %s, api socket is %s",
Klement Sekerae2636852021-03-16 12:52:12 +0100620 cls.tempdir, cls.get_api_sock_path())
621 cls.logger.debug("Random seed is %s", seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200622 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100623 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200624 cls._pcaps = []
625 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200626 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100627 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100628 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200629 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200630 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200631 # need to catch exceptions here because if we raise, then the cleanup
632 # doesn't get called and we might end with a zombie vpp
633 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100634 if cls.debug_attach:
635 cls.attach_vpp()
636 else:
637 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200638 cls.reporter.send_keep_alive(cls, 'setUpClass')
639 VppTestResult.current_test_case_info = TestCaseInfo(
640 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100641 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100642 cls.vpp_stderr_deque = deque()
Klement Sekerae2636852021-03-16 12:52:12 +0100643 if not cls.debug_attach:
644 cls.pump_thread_stop_flag = Event()
645 cls.pump_thread_wakeup_pipe = os.pipe()
646 cls.pump_thread = Thread(target=pump_output, args=(cls,))
647 cls.pump_thread.daemon = True
648 cls.pump_thread.start()
649 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400650 cls.vapi_response_timeout = 0
Ole Troan4376ab22021-03-03 10:40:05 +0100651 cls.vapi = VppPapiProvider(cls.__name__, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400652 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100653 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400654 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100655 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400656 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100657 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100658 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200659 try:
660 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100661 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200662 cls.vpp_startup_failed = True
663 cls.logger.critical(
664 "VPP died shortly after startup, check the"
665 " output to standard error for possible cause")
666 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100667 try:
668 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100669 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500670 cls.logger.debug("Exception connecting to vapi: %s" % e)
671 cls.vapi.disconnect()
672
Klement Sekera085f5c02016-11-24 01:59:16 +0100673 if cls.debug_gdbserver:
674 print(colorize("You're running VPP inside gdbserver but "
675 "VPP-API connection failed, did you forget "
676 "to 'continue' VPP from within gdb?", RED))
Ole Troan4376ab22021-03-03 10:40:05 +0100677 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100678 if cls.debug_attach:
679 last_line = cls.vapi.cli("show thread").split("\n")[-2]
680 cls.vpp_worker_count = int(last_line.split(" ")[0])
681 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400682 except vpp_papi.VPPRuntimeError as e:
683 cls.logger.debug("%s" % e)
684 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100685 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000686 except Exception as e:
687 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400688 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100689 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200690
Damjan Marionf56b77a2016-10-03 19:44:57 +0200691 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500692 def _debug_quit(cls):
693 if (cls.debug_gdbserver or cls.debug_gdb):
694 try:
695 cls.vpp.poll()
696
697 if cls.vpp.returncode is None:
698 print()
699 print(double_line_delim)
700 print("VPP or GDB server is still running")
701 print(single_line_delim)
702 input("When done debugging, press ENTER to kill the "
703 "process and finish running the testcase...")
704 except AttributeError:
705 pass
706
707 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200708 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200709 """
710 Disconnect vpp-api, kill vpp and cleanup shared memory files
711 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500712 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200713
juraj.linkes184870a2018-07-16 14:22:01 +0200714 # first signal that we want to stop the pump thread, then wake it up
715 if hasattr(cls, 'pump_thread_stop_flag'):
716 cls.pump_thread_stop_flag.set()
717 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100718 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100719 if hasattr(cls, 'pump_thread'):
720 cls.logger.debug("Waiting for pump thread to stop")
721 cls.pump_thread.join()
722 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500723 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100724 cls.vpp_stderr_reader_thread.join()
725
Klement Sekeraf62ae122016-10-11 11:47:09 +0200726 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100727 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100728 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700729 cls.logger.debug("Disconnecting class vapi client on %s",
730 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100731 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700732 cls.logger.debug("Deleting class vapi attribute on %s",
733 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100734 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200735 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100736 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000737 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100738 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400739 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100740 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100741 try:
742 outs, errs = cls.vpp.communicate(timeout=5)
743 except subprocess.TimeoutExpired:
744 cls.vpp.kill()
745 outs, errs = cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700746 cls.logger.debug("Deleting class vpp attribute on %s",
747 cls.__name__)
Klement Sekerae2636852021-03-16 12:52:12 +0100748 if not cls.debug_attach:
749 cls.vpp.stdout.close()
750 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200751 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200752
Klement Sekera3747c752017-04-10 06:30:17 +0200753 if cls.vpp_startup_failed:
754 stdout_log = cls.logger.info
755 stderr_log = cls.logger.critical
756 else:
757 stdout_log = cls.logger.info
758 stderr_log = cls.logger.info
759
Klement Sekerae4504c62016-12-08 10:16:41 +0100760 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200761 stdout_log(single_line_delim)
762 stdout_log('VPP output to stdout while running %s:', cls.__name__)
763 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100764 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200765 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
766 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200767 stdout_log('\n%s', vpp_output)
768 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200769
Klement Sekerae4504c62016-12-08 10:16:41 +0100770 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200771 stderr_log(single_line_delim)
772 stderr_log('VPP output to stderr while running %s:', cls.__name__)
773 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100774 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200775 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
776 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200777 stderr_log('\n%s', vpp_output)
778 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200779
Damjan Marionf56b77a2016-10-03 19:44:57 +0200780 @classmethod
781 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200782 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700783 cls.logger.debug("--- tearDownClass() for %s called ---" %
784 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200785 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200786 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200787 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100788 cls.reset_packet_infos()
789 if debug_framework:
790 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200791
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700792 def show_commands_at_teardown(self):
793 """ Allow subclass specific teardown logging additions."""
794 self.logger.info("--- No test specific show commands provided. ---")
795
Damjan Marionf56b77a2016-10-03 19:44:57 +0200796 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200797 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100798 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
799 (self.__class__.__name__, self._testMethodName,
800 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700801
802 try:
803 if not self.vpp_dead:
804 self.logger.debug(self.vapi.cli("show trace max 1000"))
805 self.logger.info(self.vapi.ppcli("show interface"))
806 self.logger.info(self.vapi.ppcli("show hardware"))
807 self.logger.info(self.statistics.set_errors_str())
808 self.logger.info(self.vapi.ppcli("show run"))
809 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400810 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700811 self.logger.info("Logging testcase specific show commands.")
812 self.show_commands_at_teardown()
813 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500814 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000815 m = self._testMethodName
816 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500817 tmp_api_trace = "/tmp/%s" % api_trace
818 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
819 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
820 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
821 vpp_api_trace_log))
822 os.rename(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100823 except VppTransportSocketIOError:
824 self.logger.debug("VppTransportSocketIOError: Vpp dead. "
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700825 "Cannot log show commands.")
826 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100827 else:
828 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200829
Damjan Marionf56b77a2016-10-03 19:44:57 +0200830 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200831 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800832 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200833 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100834 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400835 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
836 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100837 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100838 self.vpp_stdout_deque.append(
839 "--- test setUp() for %s.%s(%s) starts here ---\n" %
840 (self.__class__.__name__, self._testMethodName,
841 self._testMethodDoc))
842 self.vpp_stderr_deque.append(
843 "--- test setUp() for %s.%s(%s) starts here ---\n" %
844 (self.__class__.__name__, self._testMethodName,
845 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200846 self.vapi.cli("clear trace")
847 # store the test instance inside the test class - so that objects
848 # holding the class can access instance methods (like assertEqual)
849 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200850
Damjan Marionf56b77a2016-10-03 19:44:57 +0200851 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200852 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200853 """
854 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200855
Klement Sekera75e7d132017-09-20 08:26:30 +0200856 :param interfaces: iterable interface indexes (if None,
857 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200858
Klement Sekeraf62ae122016-10-11 11:47:09 +0200859 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200860 if interfaces is None:
861 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200862 for i in interfaces:
863 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200864
Damjan Marionf56b77a2016-10-03 19:44:57 +0200865 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200866 def register_pcap(cls, intf, worker):
867 """ Register a pcap in the testclass """
Klement Sekera9225dee2016-12-12 08:36:58 +0100868 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200869 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100870
871 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000872 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400873 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
874 # returns float("2.190522")
875 timestr = cls.vapi.cli('show clock')
876 head, sep, tail = timestr.partition(',')
877 head, sep, tail = head.partition('Time now')
878 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000879
880 @classmethod
881 def sleep_on_vpp_time(cls, sec):
882 """ Sleep according to time in VPP world """
883 # On a busy system with many processes
884 # we might end up with VPP time being slower than real world
885 # So take that into account when waiting for VPP to do something
886 start_time = cls.get_vpp_time()
887 while cls.get_vpp_time() - start_time < sec:
888 cls.sleep(0.1)
889
890 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100891 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000892 """ Enable the PG, wait till it is done, then clean up """
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200893 for (intf, worker) in cls._old_pcaps:
894 intf.rename_old_pcap_file(intf.get_in_path(worker),
895 intf.in_history_counter)
896 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100897 if trace:
898 cls.vapi.cli("clear trace")
899 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200900 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000901 # PG, when starts, runs to completion -
902 # so let's avoid a race condition,
903 # and wait a little till it's done.
904 # Then clean it up - and then be gone.
905 deadline = time.time() + 300
906 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
907 cls.sleep(0.01) # yield
908 if time.time() > deadline:
909 cls.logger.error("Timeout waiting for pg to stop")
910 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200911 for intf, worker in cls._pcaps:
Klement Sekera7ba9fae2021-03-31 13:36:38 +0200912 cls.vapi.cli('packet-generator delete %s' %
913 intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200914 cls._old_pcaps = cls._pcaps
915 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200916
Damjan Marionf56b77a2016-10-03 19:44:57 +0200917 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000918 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0,
919 mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200920 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100921 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200922
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100923 :param interfaces: iterable indexes of the interfaces.
924 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200925
Klement Sekeraf62ae122016-10-11 11:47:09 +0200926 """
927 result = []
928 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +0000929 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200930 setattr(cls, intf.name, intf)
931 result.append(intf)
932 cls.pg_interfaces = result
933 return result
934
Matej Klotton0178d522016-11-04 11:11:44 +0100935 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000936 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
937 pgmode = VppEnum.vl_api_pg_interface_mode_t
938 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
939 pgmode.PG_API_MODE_IP4)
940
941 @classmethod
942 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
943 pgmode = VppEnum.vl_api_pg_interface_mode_t
944 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
945 pgmode.PG_API_MODE_IP6)
946
947 @classmethod
948 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
949 pgmode = VppEnum.vl_api_pg_interface_mode_t
950 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
951 pgmode.PG_API_MODE_ETHERNET)
952
953 @classmethod
954 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
955 pgmode = VppEnum.vl_api_pg_interface_mode_t
956 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
957 pgmode.PG_API_MODE_ETHERNET)
958
959 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200960 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100961 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100962 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100963
Klement Sekerab9ef2732018-06-24 22:49:33 +0200964 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100965 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100966 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200967 result = [VppLoInterface(cls) for i in range(count)]
968 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100969 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100970 cls.lo_interfaces = result
971 return result
972
Neale Ranns192b13f2019-03-15 02:16:20 -0700973 @classmethod
974 def create_bvi_interfaces(cls, count):
975 """
976 Create BVI interfaces.
977
978 :param count: number of interfaces created.
979 :returns: List of created interfaces.
980 """
981 result = [VppBviInterface(cls) for i in range(count)]
982 for intf in result:
983 setattr(cls, intf.name, intf)
984 cls.bvi_interfaces = result
985 return result
986
Damjan Marionf56b77a2016-10-03 19:44:57 +0200987 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200988 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200989 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200990 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200991 NOTE: Currently works only when Raw layer is present.
992
993 :param packet: packet
994 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200995 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200996
997 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200998 packet_len = len(packet) + 4
999 extend = size - packet_len
1000 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +02001001 num = (extend // len(padding)) + 1
1002 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +02001003
Klement Sekeradab231a2016-12-21 08:50:14 +01001004 @classmethod
1005 def reset_packet_infos(cls):
1006 """ Reset the list of packet info objects and packet counts to zero """
1007 cls._packet_infos = {}
1008 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +02001009
Klement Sekeradab231a2016-12-21 08:50:14 +01001010 @classmethod
1011 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001012 """
1013 Create packet info object containing the source and destination indexes
1014 and add it to the testcase's packet info list
1015
Klement Sekeradab231a2016-12-21 08:50:14 +01001016 :param VppInterface src_if: source interface
1017 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +02001018
1019 :returns: _PacketInfo object
1020
1021 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001022 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +01001023 info.index = len(cls._packet_infos)
1024 info.src = src_if.sw_if_index
1025 info.dst = dst_if.sw_if_index
1026 if isinstance(dst_if, VppSubInterface):
1027 dst_idx = dst_if.parent.sw_if_index
1028 else:
1029 dst_idx = dst_if.sw_if_index
1030 if dst_idx in cls._packet_count_for_dst_if_idx:
1031 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1032 else:
1033 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1034 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001035 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001036
Damjan Marionf56b77a2016-10-03 19:44:57 +02001037 @staticmethod
1038 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001039 """
1040 Convert _PacketInfo object to packet payload
1041
1042 :param info: _PacketInfo object
1043
1044 :returns: string containing serialized data from packet info
1045 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001046
1047 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1048 return pack('iiiih', info.index, info.src,
1049 info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001050
Damjan Marionf56b77a2016-10-03 19:44:57 +02001051 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001052 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001053 """
1054 Convert packet payload to _PacketInfo object
1055
1056 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001057 :type payload: <class 'scapy.packet.Raw'>
1058 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001059 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001060 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001061 :returns: _PacketInfo object containing de-serialized data from payload
1062
1063 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001064
1065 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1066 payload_b = getattr(payload, payload_field)[:18]
1067
Damjan Marionf56b77a2016-10-03 19:44:57 +02001068 info = _PacketInfo()
Ray Kinsellab8165b92021-09-22 11:24:06 +01001069 info.index, info.src, info.dst, info.ip, info.proto \
1070 = unpack('iiiih', payload_b)
1071
1072 # some SRv6 TCs depend on get an exception if bad values are detected
1073 if info.index > 0x4000:
1074 raise ValueError('Index value is invalid')
1075
Damjan Marionf56b77a2016-10-03 19:44:57 +02001076 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001077
Damjan Marionf56b77a2016-10-03 19:44:57 +02001078 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001079 """
1080 Iterate over the packet info list stored in the testcase
1081 Start iteration with first element if info is None
1082 Continue based on index in info if info is specified
1083
1084 :param info: info or None
1085 :returns: next info in list or None if no more infos
1086 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001087 if info is None:
1088 next_index = 0
1089 else:
1090 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001091 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001092 return None
1093 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001094 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001095
Klement Sekeraf62ae122016-10-11 11:47:09 +02001096 def get_next_packet_info_for_interface(self, src_index, info):
1097 """
1098 Search the packet info list for the next packet info with same source
1099 interface index
1100
1101 :param src_index: source interface index to search for
1102 :param info: packet info - where to start the search
1103 :returns: packet info or None
1104
1105 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001106 while True:
1107 info = self.get_next_packet_info(info)
1108 if info is None:
1109 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001110 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001111 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001112
Klement Sekeraf62ae122016-10-11 11:47:09 +02001113 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1114 """
1115 Search the packet info list for the next packet info with same source
1116 and destination interface indexes
1117
1118 :param src_index: source interface index to search for
1119 :param dst_index: destination interface index to search for
1120 :param info: packet info - where to start the search
1121 :returns: packet info or None
1122
1123 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001124 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001125 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001126 if info is None:
1127 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001128 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001129 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001130
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001131 def assert_equal(self, real_value, expected_value, name_or_class=None):
1132 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001133 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001134 return
1135 try:
1136 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1137 msg = msg % (getdoc(name_or_class).strip(),
1138 real_value, str(name_or_class(real_value)),
1139 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001140 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001141 msg = "Invalid %s: %s does not match expected value %s" % (
1142 name_or_class, real_value, expected_value)
1143
1144 self.assertEqual(real_value, expected_value, msg)
1145
Klement Sekerab17dd962017-01-09 07:43:48 +01001146 def assert_in_range(self,
1147 real_value,
1148 expected_min,
1149 expected_max,
1150 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001151 if name is None:
1152 msg = None
1153 else:
1154 msg = "Invalid %s: %s out of range <%s,%s>" % (
1155 name, real_value, expected_min, expected_max)
1156 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1157
Klement Sekerad81ae412018-05-16 10:52:54 +02001158 def assert_packet_checksums_valid(self, packet,
1159 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001160 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001161 udp_layers = ['UDP', 'UDPerror']
1162 checksum_fields = ['cksum', 'chksum']
1163 checksums = []
1164 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001165 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001166 while True:
1167 layer = temp.getlayer(counter)
1168 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001169 layer = layer.copy()
1170 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001171 for cf in checksum_fields:
1172 if hasattr(layer, cf):
1173 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001174 0 == getattr(layer, cf) and \
1175 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001176 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001177 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001178 checksums.append((counter, cf))
1179 else:
1180 break
1181 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001182 if 0 == len(checksums):
1183 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001184 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001185 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001186 calc_sum = getattr(temp[layer], cf)
1187 self.assert_equal(
1188 getattr(received[layer], cf), calc_sum,
1189 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1190 self.logger.debug(
1191 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1192 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001193
1194 def assert_checksum_valid(self, received_packet, layer,
1195 field_name='chksum',
1196 ignore_zero_checksum=False):
1197 """ Check checksum of received packet on given layer """
1198 received_packet_checksum = getattr(received_packet[layer], field_name)
1199 if ignore_zero_checksum and 0 == received_packet_checksum:
1200 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001201 recalculated = received_packet.__class__(
1202 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001203 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001204 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001205 self.assert_equal(received_packet_checksum,
1206 getattr(recalculated[layer], field_name),
1207 "packet checksum on layer: %s" % layer)
1208
1209 def assert_ip_checksum_valid(self, received_packet,
1210 ignore_zero_checksum=False):
1211 self.assert_checksum_valid(received_packet, 'IP',
1212 ignore_zero_checksum=ignore_zero_checksum)
1213
1214 def assert_tcp_checksum_valid(self, received_packet,
1215 ignore_zero_checksum=False):
1216 self.assert_checksum_valid(received_packet, 'TCP',
1217 ignore_zero_checksum=ignore_zero_checksum)
1218
1219 def assert_udp_checksum_valid(self, received_packet,
1220 ignore_zero_checksum=True):
1221 self.assert_checksum_valid(received_packet, 'UDP',
1222 ignore_zero_checksum=ignore_zero_checksum)
1223
1224 def assert_embedded_icmp_checksum_valid(self, received_packet):
1225 if received_packet.haslayer(IPerror):
1226 self.assert_checksum_valid(received_packet, 'IPerror')
1227 if received_packet.haslayer(TCPerror):
1228 self.assert_checksum_valid(received_packet, 'TCPerror')
1229 if received_packet.haslayer(UDPerror):
1230 self.assert_checksum_valid(received_packet, 'UDPerror',
1231 ignore_zero_checksum=True)
1232 if received_packet.haslayer(ICMPerror):
1233 self.assert_checksum_valid(received_packet, 'ICMPerror')
1234
1235 def assert_icmp_checksum_valid(self, received_packet):
1236 self.assert_checksum_valid(received_packet, 'ICMP')
1237 self.assert_embedded_icmp_checksum_valid(received_packet)
1238
1239 def assert_icmpv6_checksum_valid(self, pkt):
1240 if pkt.haslayer(ICMPv6DestUnreach):
1241 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1242 self.assert_embedded_icmp_checksum_valid(pkt)
1243 if pkt.haslayer(ICMPv6EchoRequest):
1244 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1245 if pkt.haslayer(ICMPv6EchoReply):
1246 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1247
Klement Sekera3a343d42019-05-16 14:35:46 +02001248 def get_packet_counter(self, counter):
1249 if counter.startswith("/"):
1250 counter_value = self.statistics.get_counter(counter)
1251 else:
1252 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001253 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001254 for i in range(1, len(counters) - 1):
1255 results = counters[i].split()
1256 if results[1] == counter:
1257 counter_value = int(results[0])
1258 break
1259 return counter_value
1260
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001261 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001262 counter_value = self.get_packet_counter(counter)
1263 self.assert_equal(counter_value, expected_value,
1264 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001265
Ole Troan233e4682019-05-16 15:01:34 +02001266 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001267 counter_value = self.statistics[counter].sum()
Ole Troan233e4682019-05-16 15:01:34 +02001268 self.assert_equal(counter_value, expected_value,
1269 "error counter `%s'" % counter)
1270
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001271 @classmethod
1272 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001273
1274 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1275 # * by Guido, only the main thread can be interrupted.
1276 # */
1277 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1278 if timeout == 0:
1279 # yield quantum
1280 if hasattr(os, 'sched_yield'):
1281 os.sched_yield()
1282 else:
1283 time.sleep(0)
1284 return
1285
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001286 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001287 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001288 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001289 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001290 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001291 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001292 "slept for %es instead of ~%es!",
1293 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001294
1295 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001296 "Finished sleep (%s) - slept %es (wanted %es)",
1297 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001298
Benoît Ganne8c45e512021-02-19 16:39:13 +01001299 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001300 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001301 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001302 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001303
1304 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1305 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001306 if not timeout:
1307 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001308 for i in self.pg_interfaces:
1309 i.get_capture(0, timeout=timeout)
1310 i.assert_nothing_captured(remark=remark)
1311 timeout = 0.1
1312
Benoît Ganne8c45e512021-02-19 16:39:13 +01001313 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1314 trace=True):
Neale Rannsd7603d92019-03-28 08:56:10 +00001315 if not n_rx:
1316 n_rx = len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001317 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001318 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001319 return rx
1320
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001321 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1322 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001323 rx = output.get_capture(len(pkts))
1324 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001325 if not timeout:
1326 timeout = 1
1327 for i in self.pg_interfaces:
1328 if i not in outputs:
1329 i.get_capture(0, timeout=timeout)
1330 i.assert_nothing_captured()
1331 timeout = 0.1
1332
Neale Ranns52fae862018-01-08 04:41:42 -08001333 return rx
1334
Damjan Marionf56b77a2016-10-03 19:44:57 +02001335
juraj.linkes184870a2018-07-16 14:22:01 +02001336def get_testcase_doc_name(test):
1337 return getdoc(test.__class__).splitlines()[0]
1338
1339
Ole Trøan5ba91592018-11-22 10:01:09 +00001340def get_test_description(descriptions, test):
1341 short_description = test.shortDescription()
1342 if descriptions and short_description:
1343 return short_description
1344 else:
1345 return str(test)
1346
1347
juraj.linkes40dd73b2018-09-21 13:55:16 +02001348class TestCaseInfo(object):
1349 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1350 self.logger = logger
1351 self.tempdir = tempdir
1352 self.vpp_pid = vpp_pid
1353 self.vpp_bin_path = vpp_bin_path
1354 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001355
1356
Damjan Marionf56b77a2016-10-03 19:44:57 +02001357class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001358 """
1359 @property result_string
1360 String variable to store the test case result string.
1361 @property errors
1362 List variable containing 2-tuples of TestCase instances and strings
1363 holding formatted tracebacks. Each tuple represents a test which
1364 raised an unexpected exception.
1365 @property failures
1366 List variable containing 2-tuples of TestCase instances and strings
1367 holding formatted tracebacks. Each tuple represents a test where
1368 a failure was explicitly signalled using the TestCase.assert*()
1369 methods.
1370 """
1371
juraj.linkes40dd73b2018-09-21 13:55:16 +02001372 failed_test_cases_info = set()
1373 core_crash_test_cases_info = set()
1374 current_test_case_info = None
1375
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001376 def __init__(self, stream=None, descriptions=None, verbosity=None,
1377 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001378 """
Klement Sekerada505f62017-01-04 12:58:53 +01001379 :param stream File descriptor to store where to report test results.
1380 Set to the standard error stream by default.
1381 :param descriptions Boolean variable to store information if to use
1382 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001383 :param verbosity Integer variable to store required verbosity level.
1384 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001385 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001386 self.stream = stream
1387 self.descriptions = descriptions
1388 self.verbosity = verbosity
1389 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001390 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001391 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001392
Damjan Marionf56b77a2016-10-03 19:44:57 +02001393 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001394 """
1395 Record a test succeeded result
1396
1397 :param test:
1398
1399 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001400 if self.current_test_case_info:
1401 self.current_test_case_info.logger.debug(
1402 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1403 test._testMethodName,
1404 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001405 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001406 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001407
juraj.linkescae64f82018-09-19 15:01:47 +02001408 self.send_result_through_pipe(test, PASS)
1409
Klement Sekeraf62ae122016-10-11 11:47:09 +02001410 def addSkip(self, test, reason):
1411 """
1412 Record a test skipped.
1413
1414 :param test:
1415 :param reason:
1416
1417 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001418 if self.current_test_case_info:
1419 self.current_test_case_info.logger.debug(
1420 "--- addSkip() %s.%s(%s) called, reason is %s" %
1421 (test.__class__.__name__, test._testMethodName,
1422 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001423 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001424 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001425
Klement Sekera558ceab2021-04-08 19:37:41 +02001426 if reason == "not enough cpus":
1427 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1428 else:
1429 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001430
juraj.linkes40dd73b2018-09-21 13:55:16 +02001431 def symlink_failed(self):
1432 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001433 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001434 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001435 link_path = os.path.join(
1436 failed_dir,
1437 '%s-FAILED' %
1438 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001439
1440 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001441 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001442 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001443 "os.symlink(%s, %s)" %
1444 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001445 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001446 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001447 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001448 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001449 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001450
Klement Sekeraf413bef2017-08-15 07:09:02 +02001451 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001452 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001453
juraj.linkescae64f82018-09-19 15:01:47 +02001454 def send_result_through_pipe(self, test, result):
1455 if hasattr(self, 'test_framework_result_pipe'):
1456 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001457 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001458 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001459
juraj.linkes40dd73b2018-09-21 13:55:16 +02001460 def log_error(self, test, err, fn_name):
1461 if self.current_test_case_info:
1462 if isinstance(test, unittest.suite._ErrorHolder):
1463 test_name = test.description
1464 else:
1465 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1466 test._testMethodName,
1467 test._testMethodDoc)
1468 self.current_test_case_info.logger.debug(
1469 "--- %s() %s called, err is %s" %
1470 (fn_name, test_name, err))
1471 self.current_test_case_info.logger.debug(
1472 "formatted exception is:\n%s" %
1473 "".join(format_exception(*err)))
1474
1475 def add_error(self, test, err, unittest_fn, error_type):
1476 if error_type == FAIL:
1477 self.log_error(test, err, 'addFailure')
1478 error_type_str = colorize("FAIL", RED)
1479 elif error_type == ERROR:
1480 self.log_error(test, err, 'addError')
1481 error_type_str = colorize("ERROR", RED)
1482 else:
1483 raise Exception('Error type %s cannot be used to record an '
1484 'error or a failure' % error_type)
1485
1486 unittest_fn(self, test, err)
1487 if self.current_test_case_info:
1488 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1489 (error_type_str,
1490 self.current_test_case_info.tempdir)
1491 self.symlink_failed()
1492 self.failed_test_cases_info.add(self.current_test_case_info)
1493 if is_core_present(self.current_test_case_info.tempdir):
1494 if not self.current_test_case_info.core_crash_test:
1495 if isinstance(test, unittest.suite._ErrorHolder):
1496 test_name = str(test)
1497 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001498 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001499 get_testcase_doc_name(test), test.id())
1500 self.current_test_case_info.core_crash_test = test_name
1501 self.core_crash_test_cases_info.add(
1502 self.current_test_case_info)
1503 else:
1504 self.result_string = '%s [no temp dir]' % error_type_str
1505
1506 self.send_result_through_pipe(test, error_type)
1507
Damjan Marionf56b77a2016-10-03 19:44:57 +02001508 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001509 """
1510 Record a test failed result
1511
1512 :param test:
1513 :param err: error message
1514
1515 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001516 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001517
Damjan Marionf56b77a2016-10-03 19:44:57 +02001518 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001519 """
1520 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001521
Klement Sekeraf62ae122016-10-11 11:47:09 +02001522 :param test:
1523 :param err: error message
1524
1525 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001526 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001527
Damjan Marionf56b77a2016-10-03 19:44:57 +02001528 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001529 """
1530 Get test description
1531
1532 :param test:
1533 :returns: test description
1534
1535 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001536 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001537
Damjan Marionf56b77a2016-10-03 19:44:57 +02001538 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001539 """
1540 Start a test
1541
1542 :param test:
1543
1544 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001545
1546 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001547 if test.__class__ in self.printed:
1548 return
1549
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001550 test_doc = getdoc(test)
1551 if not test_doc:
1552 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001553
Klement Sekeraea6236b2021-03-25 14:03:44 +01001554 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001555 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001556 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001557 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001558
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001559 # This block may overwrite the colorized title above,
1560 # but we want this to stand out and be fixed
1561 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekera558ceab2021-04-08 19:37:41 +02001562 test_title = colorize(
1563 f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001564
Klement Sekeraea6236b2021-03-25 14:03:44 +01001565 if hasattr(test, 'vpp_worker_count'):
1566 if test.vpp_worker_count == 0:
1567 test_title += " [main thread only]"
1568 elif test.vpp_worker_count == 1:
1569 test_title += " [1 worker thread]"
1570 else:
1571 test_title += f" [{test.vpp_worker_count} worker threads]"
1572
Klement Sekera558ceab2021-04-08 19:37:41 +02001573 if test.__class__.skipped_due_to_cpu_lack:
1574 test_title = colorize(
1575 f"{test_title} [skipped - not enough cpus, "
1576 f"required={test.__class__.get_cpus_required()}, "
1577 f"available={max_vpp_cpus}]", YELLOW)
1578
1579 print(double_line_delim)
1580 print(test_title)
1581 print(double_line_delim)
1582 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001583
1584 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001585 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001586 unittest.TestResult.startTest(self, test)
1587 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001588 self.stream.writeln(
1589 "Starting " + self.getDescription(test) + " ...")
1590 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001591
Damjan Marionf56b77a2016-10-03 19:44:57 +02001592 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001593 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001594 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001595
1596 :param test:
1597
1598 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001599 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001600
Damjan Marionf56b77a2016-10-03 19:44:57 +02001601 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001602 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001603 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001604 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001605 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001606 else:
Ole Troan0c629322019-11-28 14:48:44 +01001607 self.stream.writeln("%-68s %4.2f %s" %
1608 (self.getDescription(test),
1609 time.time() - self.start_test,
1610 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001611
1612 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001613
Damjan Marionf56b77a2016-10-03 19:44:57 +02001614 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001615 """
1616 Print errors from running the test case
1617 """
juraj.linkesabec0122018-11-16 17:28:56 +01001618 if len(self.errors) > 0 or len(self.failures) > 0:
1619 self.stream.writeln()
1620 self.printErrorList('ERROR', self.errors)
1621 self.printErrorList('FAIL', self.failures)
1622
1623 # ^^ that is the last output from unittest before summary
1624 if not self.runner.print_summary:
1625 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1626 self.stream = devnull
1627 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001628
Damjan Marionf56b77a2016-10-03 19:44:57 +02001629 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001630 """
1631 Print error list to the output stream together with error type
1632 and test case description.
1633
1634 :param flavour: error type
1635 :param errors: iterable errors
1636
1637 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001638 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001639 self.stream.writeln(double_line_delim)
1640 self.stream.writeln("%s: %s" %
1641 (flavour, self.getDescription(test)))
1642 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001643 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001644
1645
Damjan Marionf56b77a2016-10-03 19:44:57 +02001646class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001647 """
Klement Sekera104543f2017-02-03 07:29:43 +01001648 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001649 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001650
Klement Sekeraf62ae122016-10-11 11:47:09 +02001651 @property
1652 def resultclass(self):
1653 """Class maintaining the results of the tests"""
1654 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001655
juraj.linkes184870a2018-07-16 14:22:01 +02001656 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001657 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001658 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001659 # ignore stream setting here, use hard-coded stdout to be in sync
1660 # with prints from VppTestCase methods ...
1661 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1662 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001663 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001664 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001665
juraj.linkesabec0122018-11-16 17:28:56 +01001666 self.orig_stream = self.stream
1667 self.resultclass.test_framework_result_pipe = result_pipe
1668
1669 self.print_summary = print_summary
1670
1671 def _makeResult(self):
1672 return self.resultclass(self.stream,
1673 self.descriptions,
1674 self.verbosity,
1675 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001676
Damjan Marionf56b77a2016-10-03 19:44:57 +02001677 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001678 """
1679 Run the tests
1680
1681 :param test:
1682
1683 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001684 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001685
1686 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001687 if not self.print_summary:
1688 self.stream = self.orig_stream
1689 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001690 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001691
1692
1693class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001694 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1695 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001696 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001697 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001698 if hasattr(self, 'testcase') and self.testcase.debug_all:
1699 if self.testcase.debug_gdbserver:
1700 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1701 .format(port=self.testcase.gdbserver_port)] + args
1702 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1703 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001704 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001705 self.app_name = os.path.basename(self.app_bin)
1706 if hasattr(self, 'role'):
1707 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001708 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001709 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001710 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001711 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001712
Dave Wallace24564332019-10-21 02:53:14 +00001713 def wait_for_enter(self):
1714 if not hasattr(self, 'testcase'):
1715 return
1716 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1717 print()
1718 print(double_line_delim)
1719 print("Spawned GDB Server for '{app}' with PID: {pid}"
1720 .format(app=self.app_name, pid=self.process.pid))
1721 elif self.testcase.debug_all and self.testcase.debug_gdb:
1722 print()
1723 print(double_line_delim)
1724 print("Spawned '{app}' with PID: {pid}"
1725 .format(app=self.app_name, pid=self.process.pid))
1726 else:
1727 return
1728 print(single_line_delim)
1729 print("You can debug '{app}' using:".format(app=self.app_name))
1730 if self.testcase.debug_gdbserver:
1731 print("sudo gdb " + self.app_bin +
1732 " -ex 'target remote localhost:{port}'"
1733 .format(port=self.testcase.gdbserver_port))
1734 print("Now is the time to attach gdb by running the above "
1735 "command, set up breakpoints etc., then resume from "
1736 "within gdb by issuing the 'continue' command")
1737 self.testcase.gdbserver_port += 1
1738 elif self.testcase.debug_gdb:
1739 print("sudo gdb " + self.app_bin +
1740 " -ex 'attach {pid}'".format(pid=self.process.pid))
1741 print("Now is the time to attach gdb by running the above "
1742 "command and set up breakpoints etc., then resume from"
1743 " within gdb by issuing the 'continue' command")
1744 print(single_line_delim)
1745 input("Press ENTER to continue running the testcase...")
1746
Neale Ranns812ed392017-10-16 04:20:13 -07001747 def run(self):
1748 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001749 if not os.path.exists(executable) or not os.access(
1750 executable, os.F_OK | os.X_OK):
1751 # Exit code that means some system file did not exist,
1752 # could not be opened, or had some other kind of error.
1753 self.result = os.EX_OSFILE
1754 raise EnvironmentError(
1755 "executable '%s' is not found or executable." % executable)
Dave Wallace4ee17d82021-05-20 14:01:51 -04001756 self.logger.debug("Running executable '{app}': '{cmd}'"
1757 .format(app=self.app_name,
1758 cmd=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001759 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001760 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001761 env["CK_LOG_FILE_NAME"] = "-"
1762 self.process = subprocess.Popen(
Dave Wallaceae8d3322021-05-18 17:12:16 -04001763 ['stdbuf', '-o0', '-e0'] + self.args, shell=False, env=env,
1764 preexec_fn=os.setpgrp, stdout=subprocess.PIPE,
1765 stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001766 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001767 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001768 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001769 self.logger.info("Return code is `%s'" % self.process.returncode)
1770 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001771 self.logger.info("Executable `{app}' wrote to stdout:"
1772 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001773 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001774 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001775 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001776 self.logger.info("Executable `{app}' wrote to stderr:"
1777 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001778 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001779 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001780 self.logger.info(single_line_delim)
1781 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001782
Klement Sekera6aa58b72019-05-16 14:34:55 +02001783
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001784if __name__ == '__main__':
1785 pass