blob: 02e455b1223a2874fcecf4d77864d3048f74c03c [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")
Damjan Marion5546e432021-09-30 20:04:14 +0200418 extern_plugin_path = os.getenv('EXTERN_PLUGINS')
Ole Troana45dc072018-12-21 16:04:22 +0100419 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100420 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100421 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100422 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100423 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100424 if size is not None:
425 coredump_size = "coredump-size %s" % size
426 if coredump_size is None:
427 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200428
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000429 default_variant = os.getenv("VARIANT")
430 if default_variant is not None:
431 default_variant = "defaults { %s 100 }" % default_variant
432 else:
433 default_variant = ""
434
Dave Barach77841402020-04-29 17:04:10 -0400435 api_fuzzing = os.getenv("API_FUZZ")
436 if api_fuzzing is None:
437 api_fuzzing = 'off'
438
Klement Sekera8d815022021-03-15 16:58:10 +0100439 cls.vpp_cmdline = [
440 cls.vpp_bin,
441 "unix", "{", "nodaemon", debug_cli, "full-coredump",
442 coredump_size, "runtime-dir", cls.tempdir, "}",
443 "api-trace", "{", "on", "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100444 "api-segment", "{", "prefix", cls.get_api_segment_prefix(), "}",
Klement Sekera558ceab2021-04-08 19:37:41 +0200445 "cpu", "{", "main-core", str(cls.cpus[0]), ]
Damjan Marion5546e432021-09-30 20:04:14 +0200446 if extern_plugin_path is not None:
447 cls.extra_vpp_plugin_config.append(
448 "add-path %s" % extern_plugin_path)
Klement Sekera558ceab2021-04-08 19:37:41 +0200449 if cls.get_vpp_worker_count():
450 cls.vpp_cmdline.extend([
451 "corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])])
Klement Sekera8d815022021-03-15 16:58:10 +0100452 cls.vpp_cmdline.extend([
453 "}",
454 "physmem", "{", "max-size", "32m", "}",
Arthur de Kerhordb023802021-03-11 10:26:54 -0800455 "statseg", "{", "socket-name", cls.get_stats_sock_path(),
456 cls.extra_vpp_statseg_config, "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100457 "socksvr", "{", "socket-name", cls.get_api_sock_path(), "}",
Klement Sekera8d815022021-03-15 16:58:10 +0100458 "node { ", default_variant, "}",
459 "api-fuzz {", api_fuzzing, "}",
460 "plugins", "{", "plugin", "dpdk_plugin.so", "{", "disable", "}",
461 "plugin", "rdma_plugin.so", "{", "disable", "}",
462 "plugin", "lisp_unittest_plugin.so", "{", "enable", "}",
463 "plugin", "unittest_plugin.so", "{", "enable", "}"
464 ] + cls.extra_vpp_plugin_config + ["}", ])
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000465
Ole Troana45dc072018-12-21 16:04:22 +0100466 if cls.extra_vpp_punt_config is not None:
467 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Dave Barach7d31ab22019-05-08 19:18:18 -0400468
Klement Sekerae2636852021-03-16 12:52:12 +0100469 if not cls.debug_attach:
470 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
471 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200472
473 @classmethod
474 def wait_for_enter(cls):
475 if cls.debug_gdbserver:
476 print(double_line_delim)
477 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
478 elif cls.debug_gdb:
479 print(double_line_delim)
480 print("Spawned VPP with PID: %d" % cls.vpp.pid)
481 else:
482 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
483 return
484 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000485 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200486 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400487 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000488 " -ex 'target remote localhost:{port}'"
489 .format(port=cls.gdbserver_port))
490 print("Now is the time to attach gdb by running the above "
491 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200492 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000493 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200494 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400495 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000496 print("Now is the time to attach gdb by running the above "
497 "command and set up breakpoints etc., then resume VPP from"
498 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200499 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800500 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200501
502 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100503 def attach_vpp(cls):
504 cls.vpp = DummyVpp()
505
506 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200507 def run_vpp(cls):
Klement Sekera558ceab2021-04-08 19:37:41 +0200508 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200509 cmdline = cls.vpp_cmdline
510
511 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100512 gdbserver = '/usr/bin/gdbserver'
Klement Sekera558ceab2021-04-08 19:37:41 +0200513 if not os.path.isfile(gdbserver) or\
Klement Sekera931be3a2016-11-03 05:36:01 +0100514 not os.access(gdbserver, os.X_OK):
515 raise Exception("gdbserver binary '%s' does not exist or is "
516 "not executable" % gdbserver)
517
Dave Wallace24564332019-10-21 02:53:14 +0000518 cmdline = [gdbserver, 'localhost:{port}'
519 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200520 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
521
Klement Sekera931be3a2016-11-03 05:36:01 +0100522 try:
523 cls.vpp = subprocess.Popen(cmdline,
524 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100525 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800526 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800527 cls.logger.critical("Subprocess returned with non-0 return code: ("
528 "%s)", e.returncode)
529 raise
530 except OSError as e:
531 cls.logger.critical("Subprocess returned with OS error: "
532 "(%s) %s", e.errno, e.strerror)
533 raise
534 except Exception as e:
535 cls.logger.exception("Subprocess returned unexpected from "
536 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100537 raise
538
Klement Sekera277b89c2016-10-28 13:20:27 +0200539 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100540
Damjan Marionf56b77a2016-10-03 19:44:57 +0200541 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000542 def wait_for_coredump(cls):
543 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400544 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000545 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400546 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000547 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400548 ok = False
549 while time.time() < deadline:
550 cls.sleep(1)
551 size = curr_size
552 curr_size = os.path.getsize(corefile)
553 if size == curr_size:
554 ok = True
555 break
556 if not ok:
557 cls.logger.error("Timed out waiting for coredump to complete:"
558 " %s", corefile)
559 else:
560 cls.logger.error("Coredump complete: %s, size %d",
561 corefile, curr_size)
562
563 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100564 def get_stats_sock_path(cls):
565 return "%s/stats.sock" % cls.tempdir
566
567 @classmethod
568 def get_api_sock_path(cls):
569 return "%s/api.sock" % cls.tempdir
570
571 @classmethod
572 def get_api_segment_prefix(cls):
573 return os.path.basename(cls.tempdir) # Only used for VAPI
574
575 @classmethod
576 def get_tempdir(cls):
577 if cls.debug_attach:
578 return os.getenv("VPP_IN_GDB_TMP_DIR",
579 "/tmp/unittest-attach-gdb")
580 else:
581 return tempfile.mkdtemp(prefix='vpp-unittest-%s-' % cls.__name__)
582
583 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200584 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200585 """
586 Perform class setup before running the testcase
587 Remove shared memory files, start vpp and connect the vpp-api
588 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800589 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100590 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000591 seed = os.environ["RND_SEED"]
592 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100593 if hasattr(cls, 'parallel_handler'):
594 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100595 cls.logger.propagate = False
Klement Sekerae2636852021-03-16 12:52:12 +0100596 d = os.getenv("DEBUG", None)
597 cls.set_debug_flags(d)
598 cls.tempdir = cls.get_tempdir()
Klement Sekera027dbd52017-04-11 06:01:53 +0200599 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
600 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100601 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
602 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200603 cls.file_handler.setLevel(DEBUG)
604 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100605 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200606 os.chdir(cls.tempdir)
Ole Troan4376ab22021-03-03 10:40:05 +0100607 cls.logger.info("Temporary dir is %s, api socket is %s",
Klement Sekerae2636852021-03-16 12:52:12 +0100608 cls.tempdir, cls.get_api_sock_path())
609 cls.logger.debug("Random seed is %s", seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200610 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100611 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200612 cls._pcaps = []
613 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200614 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100615 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100616 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200617 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200618 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200619 # need to catch exceptions here because if we raise, then the cleanup
620 # doesn't get called and we might end with a zombie vpp
621 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100622 if cls.debug_attach:
623 cls.attach_vpp()
624 else:
625 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200626 cls.reporter.send_keep_alive(cls, 'setUpClass')
627 VppTestResult.current_test_case_info = TestCaseInfo(
628 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100629 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100630 cls.vpp_stderr_deque = deque()
Klement Sekerae2636852021-03-16 12:52:12 +0100631 if not cls.debug_attach:
632 cls.pump_thread_stop_flag = Event()
633 cls.pump_thread_wakeup_pipe = os.pipe()
634 cls.pump_thread = Thread(target=pump_output, args=(cls,))
635 cls.pump_thread.daemon = True
636 cls.pump_thread.start()
637 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400638 cls.vapi_response_timeout = 0
Ole Troan4376ab22021-03-03 10:40:05 +0100639 cls.vapi = VppPapiProvider(cls.__name__, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400640 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100641 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400642 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100643 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400644 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100645 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100646 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200647 try:
648 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100649 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200650 cls.vpp_startup_failed = True
651 cls.logger.critical(
652 "VPP died shortly after startup, check the"
653 " output to standard error for possible cause")
654 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100655 try:
656 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100657 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500658 cls.logger.debug("Exception connecting to vapi: %s" % e)
659 cls.vapi.disconnect()
660
Klement Sekera085f5c02016-11-24 01:59:16 +0100661 if cls.debug_gdbserver:
662 print(colorize("You're running VPP inside gdbserver but "
663 "VPP-API connection failed, did you forget "
664 "to 'continue' VPP from within gdb?", RED))
Ole Troan4376ab22021-03-03 10:40:05 +0100665 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100666 if cls.debug_attach:
667 last_line = cls.vapi.cli("show thread").split("\n")[-2]
668 cls.vpp_worker_count = int(last_line.split(" ")[0])
669 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400670 except vpp_papi.VPPRuntimeError as e:
671 cls.logger.debug("%s" % e)
672 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100673 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000674 except Exception as e:
675 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400676 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100677 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200678
Damjan Marionf56b77a2016-10-03 19:44:57 +0200679 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500680 def _debug_quit(cls):
681 if (cls.debug_gdbserver or cls.debug_gdb):
682 try:
683 cls.vpp.poll()
684
685 if cls.vpp.returncode is None:
686 print()
687 print(double_line_delim)
688 print("VPP or GDB server is still running")
689 print(single_line_delim)
690 input("When done debugging, press ENTER to kill the "
691 "process and finish running the testcase...")
692 except AttributeError:
693 pass
694
695 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200696 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200697 """
698 Disconnect vpp-api, kill vpp and cleanup shared memory files
699 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500700 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200701
juraj.linkes184870a2018-07-16 14:22:01 +0200702 # first signal that we want to stop the pump thread, then wake it up
703 if hasattr(cls, 'pump_thread_stop_flag'):
704 cls.pump_thread_stop_flag.set()
705 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100706 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100707 if hasattr(cls, 'pump_thread'):
708 cls.logger.debug("Waiting for pump thread to stop")
709 cls.pump_thread.join()
710 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500711 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100712 cls.vpp_stderr_reader_thread.join()
713
Klement Sekeraf62ae122016-10-11 11:47:09 +0200714 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100715 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100716 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700717 cls.logger.debug("Disconnecting class vapi client on %s",
718 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100719 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700720 cls.logger.debug("Deleting class vapi attribute on %s",
721 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100722 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200723 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100724 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000725 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100726 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400727 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100728 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100729 try:
730 outs, errs = cls.vpp.communicate(timeout=5)
731 except subprocess.TimeoutExpired:
732 cls.vpp.kill()
733 outs, errs = cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700734 cls.logger.debug("Deleting class vpp attribute on %s",
735 cls.__name__)
Klement Sekerae2636852021-03-16 12:52:12 +0100736 if not cls.debug_attach:
737 cls.vpp.stdout.close()
738 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200739 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200740
Klement Sekera3747c752017-04-10 06:30:17 +0200741 if cls.vpp_startup_failed:
742 stdout_log = cls.logger.info
743 stderr_log = cls.logger.critical
744 else:
745 stdout_log = cls.logger.info
746 stderr_log = cls.logger.info
747
Klement Sekerae4504c62016-12-08 10:16:41 +0100748 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200749 stdout_log(single_line_delim)
750 stdout_log('VPP output to stdout while running %s:', cls.__name__)
751 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100752 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200753 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
754 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200755 stdout_log('\n%s', vpp_output)
756 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200757
Klement Sekerae4504c62016-12-08 10:16:41 +0100758 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200759 stderr_log(single_line_delim)
760 stderr_log('VPP output to stderr while running %s:', cls.__name__)
761 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100762 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200763 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
764 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200765 stderr_log('\n%s', vpp_output)
766 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200767
Damjan Marionf56b77a2016-10-03 19:44:57 +0200768 @classmethod
769 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200770 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700771 cls.logger.debug("--- tearDownClass() for %s called ---" %
772 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200773 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200774 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200775 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100776 cls.reset_packet_infos()
777 if debug_framework:
778 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200779
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700780 def show_commands_at_teardown(self):
781 """ Allow subclass specific teardown logging additions."""
782 self.logger.info("--- No test specific show commands provided. ---")
783
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200785 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100786 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
787 (self.__class__.__name__, self._testMethodName,
788 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700789
790 try:
791 if not self.vpp_dead:
792 self.logger.debug(self.vapi.cli("show trace max 1000"))
793 self.logger.info(self.vapi.ppcli("show interface"))
794 self.logger.info(self.vapi.ppcli("show hardware"))
795 self.logger.info(self.statistics.set_errors_str())
796 self.logger.info(self.vapi.ppcli("show run"))
797 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400798 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700799 self.logger.info("Logging testcase specific show commands.")
800 self.show_commands_at_teardown()
801 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500802 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000803 m = self._testMethodName
804 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500805 tmp_api_trace = "/tmp/%s" % api_trace
806 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
807 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
808 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
809 vpp_api_trace_log))
810 os.rename(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100811 except VppTransportSocketIOError:
812 self.logger.debug("VppTransportSocketIOError: Vpp dead. "
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700813 "Cannot log show commands.")
814 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100815 else:
816 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200817
Damjan Marionf56b77a2016-10-03 19:44:57 +0200818 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200819 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800820 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200821 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100822 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400823 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
824 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100825 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100826 self.vpp_stdout_deque.append(
827 "--- test setUp() for %s.%s(%s) starts here ---\n" %
828 (self.__class__.__name__, self._testMethodName,
829 self._testMethodDoc))
830 self.vpp_stderr_deque.append(
831 "--- test setUp() for %s.%s(%s) starts here ---\n" %
832 (self.__class__.__name__, self._testMethodName,
833 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200834 self.vapi.cli("clear trace")
835 # store the test instance inside the test class - so that objects
836 # holding the class can access instance methods (like assertEqual)
837 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200838
Damjan Marionf56b77a2016-10-03 19:44:57 +0200839 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200840 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200841 """
842 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200843
Klement Sekera75e7d132017-09-20 08:26:30 +0200844 :param interfaces: iterable interface indexes (if None,
845 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200846
Klement Sekeraf62ae122016-10-11 11:47:09 +0200847 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200848 if interfaces is None:
849 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200850 for i in interfaces:
851 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200852
Damjan Marionf56b77a2016-10-03 19:44:57 +0200853 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200854 def register_pcap(cls, intf, worker):
855 """ Register a pcap in the testclass """
Klement Sekera9225dee2016-12-12 08:36:58 +0100856 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200857 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100858
859 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000860 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400861 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
862 # returns float("2.190522")
863 timestr = cls.vapi.cli('show clock')
864 head, sep, tail = timestr.partition(',')
865 head, sep, tail = head.partition('Time now')
866 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000867
868 @classmethod
869 def sleep_on_vpp_time(cls, sec):
870 """ Sleep according to time in VPP world """
871 # On a busy system with many processes
872 # we might end up with VPP time being slower than real world
873 # So take that into account when waiting for VPP to do something
874 start_time = cls.get_vpp_time()
875 while cls.get_vpp_time() - start_time < sec:
876 cls.sleep(0.1)
877
878 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100879 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000880 """ Enable the PG, wait till it is done, then clean up """
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200881 for (intf, worker) in cls._old_pcaps:
882 intf.rename_old_pcap_file(intf.get_in_path(worker),
883 intf.in_history_counter)
884 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100885 if trace:
886 cls.vapi.cli("clear trace")
887 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200888 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000889 # PG, when starts, runs to completion -
890 # so let's avoid a race condition,
891 # and wait a little till it's done.
892 # Then clean it up - and then be gone.
893 deadline = time.time() + 300
894 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
895 cls.sleep(0.01) # yield
896 if time.time() > deadline:
897 cls.logger.error("Timeout waiting for pg to stop")
898 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200899 for intf, worker in cls._pcaps:
Klement Sekera7ba9fae2021-03-31 13:36:38 +0200900 cls.vapi.cli('packet-generator delete %s' %
901 intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200902 cls._old_pcaps = cls._pcaps
903 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200904
Damjan Marionf56b77a2016-10-03 19:44:57 +0200905 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000906 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0,
907 mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200908 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100909 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200910
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100911 :param interfaces: iterable indexes of the interfaces.
912 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200913
Klement Sekeraf62ae122016-10-11 11:47:09 +0200914 """
915 result = []
916 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +0000917 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200918 setattr(cls, intf.name, intf)
919 result.append(intf)
920 cls.pg_interfaces = result
921 return result
922
Matej Klotton0178d522016-11-04 11:11:44 +0100923 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000924 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
925 pgmode = VppEnum.vl_api_pg_interface_mode_t
926 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
927 pgmode.PG_API_MODE_IP4)
928
929 @classmethod
930 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
931 pgmode = VppEnum.vl_api_pg_interface_mode_t
932 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
933 pgmode.PG_API_MODE_IP6)
934
935 @classmethod
936 def create_pg_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_ETHERNET)
940
941 @classmethod
942 def create_pg_ethernet_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_ETHERNET)
946
947 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200948 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100949 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100950 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100951
Klement Sekerab9ef2732018-06-24 22:49:33 +0200952 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100953 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100954 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200955 result = [VppLoInterface(cls) for i in range(count)]
956 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100957 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100958 cls.lo_interfaces = result
959 return result
960
Neale Ranns192b13f2019-03-15 02:16:20 -0700961 @classmethod
962 def create_bvi_interfaces(cls, count):
963 """
964 Create BVI interfaces.
965
966 :param count: number of interfaces created.
967 :returns: List of created interfaces.
968 """
969 result = [VppBviInterface(cls) for i in range(count)]
970 for intf in result:
971 setattr(cls, intf.name, intf)
972 cls.bvi_interfaces = result
973 return result
974
Damjan Marionf56b77a2016-10-03 19:44:57 +0200975 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200976 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200977 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200978 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200979 NOTE: Currently works only when Raw layer is present.
980
981 :param packet: packet
982 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200983 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200984
985 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200986 packet_len = len(packet) + 4
987 extend = size - packet_len
988 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200989 num = (extend // len(padding)) + 1
990 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200991
Klement Sekeradab231a2016-12-21 08:50:14 +0100992 @classmethod
993 def reset_packet_infos(cls):
994 """ Reset the list of packet info objects and packet counts to zero """
995 cls._packet_infos = {}
996 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200997
Klement Sekeradab231a2016-12-21 08:50:14 +0100998 @classmethod
999 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001000 """
1001 Create packet info object containing the source and destination indexes
1002 and add it to the testcase's packet info list
1003
Klement Sekeradab231a2016-12-21 08:50:14 +01001004 :param VppInterface src_if: source interface
1005 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +02001006
1007 :returns: _PacketInfo object
1008
1009 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001010 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +01001011 info.index = len(cls._packet_infos)
1012 info.src = src_if.sw_if_index
1013 info.dst = dst_if.sw_if_index
1014 if isinstance(dst_if, VppSubInterface):
1015 dst_idx = dst_if.parent.sw_if_index
1016 else:
1017 dst_idx = dst_if.sw_if_index
1018 if dst_idx in cls._packet_count_for_dst_if_idx:
1019 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1020 else:
1021 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1022 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001023 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001024
Damjan Marionf56b77a2016-10-03 19:44:57 +02001025 @staticmethod
1026 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001027 """
1028 Convert _PacketInfo object to packet payload
1029
1030 :param info: _PacketInfo object
1031
1032 :returns: string containing serialized data from packet info
1033 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001034
1035 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1036 return pack('iiiih', info.index, info.src,
1037 info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001038
Damjan Marionf56b77a2016-10-03 19:44:57 +02001039 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001040 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001041 """
1042 Convert packet payload to _PacketInfo object
1043
1044 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001045 :type payload: <class 'scapy.packet.Raw'>
1046 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001047 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001048 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001049 :returns: _PacketInfo object containing de-serialized data from payload
1050
1051 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001052
1053 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1054 payload_b = getattr(payload, payload_field)[:18]
1055
Damjan Marionf56b77a2016-10-03 19:44:57 +02001056 info = _PacketInfo()
Ray Kinsellab8165b92021-09-22 11:24:06 +01001057 info.index, info.src, info.dst, info.ip, info.proto \
1058 = unpack('iiiih', payload_b)
1059
1060 # some SRv6 TCs depend on get an exception if bad values are detected
1061 if info.index > 0x4000:
1062 raise ValueError('Index value is invalid')
1063
Damjan Marionf56b77a2016-10-03 19:44:57 +02001064 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001065
Damjan Marionf56b77a2016-10-03 19:44:57 +02001066 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001067 """
1068 Iterate over the packet info list stored in the testcase
1069 Start iteration with first element if info is None
1070 Continue based on index in info if info is specified
1071
1072 :param info: info or None
1073 :returns: next info in list or None if no more infos
1074 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001075 if info is None:
1076 next_index = 0
1077 else:
1078 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001079 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001080 return None
1081 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001082 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001083
Klement Sekeraf62ae122016-10-11 11:47:09 +02001084 def get_next_packet_info_for_interface(self, src_index, info):
1085 """
1086 Search the packet info list for the next packet info with same source
1087 interface index
1088
1089 :param src_index: source interface index to search for
1090 :param info: packet info - where to start the search
1091 :returns: packet info or None
1092
1093 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001094 while True:
1095 info = self.get_next_packet_info(info)
1096 if info is None:
1097 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001098 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001099 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001100
Klement Sekeraf62ae122016-10-11 11:47:09 +02001101 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1102 """
1103 Search the packet info list for the next packet info with same source
1104 and destination interface indexes
1105
1106 :param src_index: source interface index to search for
1107 :param dst_index: destination interface index to search for
1108 :param info: packet info - where to start the search
1109 :returns: packet info or None
1110
1111 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001112 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001113 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001114 if info is None:
1115 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001116 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001117 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001118
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001119 def assert_equal(self, real_value, expected_value, name_or_class=None):
1120 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001121 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001122 return
1123 try:
1124 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1125 msg = msg % (getdoc(name_or_class).strip(),
1126 real_value, str(name_or_class(real_value)),
1127 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001128 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001129 msg = "Invalid %s: %s does not match expected value %s" % (
1130 name_or_class, real_value, expected_value)
1131
1132 self.assertEqual(real_value, expected_value, msg)
1133
Klement Sekerab17dd962017-01-09 07:43:48 +01001134 def assert_in_range(self,
1135 real_value,
1136 expected_min,
1137 expected_max,
1138 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001139 if name is None:
1140 msg = None
1141 else:
1142 msg = "Invalid %s: %s out of range <%s,%s>" % (
1143 name, real_value, expected_min, expected_max)
1144 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1145
Klement Sekerad81ae412018-05-16 10:52:54 +02001146 def assert_packet_checksums_valid(self, packet,
1147 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001148 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001149 udp_layers = ['UDP', 'UDPerror']
1150 checksum_fields = ['cksum', 'chksum']
1151 checksums = []
1152 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001153 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001154 while True:
1155 layer = temp.getlayer(counter)
1156 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001157 layer = layer.copy()
1158 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001159 for cf in checksum_fields:
1160 if hasattr(layer, cf):
1161 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001162 0 == getattr(layer, cf) and \
1163 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001164 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001165 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001166 checksums.append((counter, cf))
1167 else:
1168 break
1169 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001170 if 0 == len(checksums):
1171 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001172 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001173 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001174 calc_sum = getattr(temp[layer], cf)
1175 self.assert_equal(
1176 getattr(received[layer], cf), calc_sum,
1177 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1178 self.logger.debug(
1179 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1180 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001181
1182 def assert_checksum_valid(self, received_packet, layer,
1183 field_name='chksum',
1184 ignore_zero_checksum=False):
1185 """ Check checksum of received packet on given layer """
1186 received_packet_checksum = getattr(received_packet[layer], field_name)
1187 if ignore_zero_checksum and 0 == received_packet_checksum:
1188 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001189 recalculated = received_packet.__class__(
1190 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001191 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001192 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001193 self.assert_equal(received_packet_checksum,
1194 getattr(recalculated[layer], field_name),
1195 "packet checksum on layer: %s" % layer)
1196
1197 def assert_ip_checksum_valid(self, received_packet,
1198 ignore_zero_checksum=False):
1199 self.assert_checksum_valid(received_packet, 'IP',
1200 ignore_zero_checksum=ignore_zero_checksum)
1201
1202 def assert_tcp_checksum_valid(self, received_packet,
1203 ignore_zero_checksum=False):
1204 self.assert_checksum_valid(received_packet, 'TCP',
1205 ignore_zero_checksum=ignore_zero_checksum)
1206
1207 def assert_udp_checksum_valid(self, received_packet,
1208 ignore_zero_checksum=True):
1209 self.assert_checksum_valid(received_packet, 'UDP',
1210 ignore_zero_checksum=ignore_zero_checksum)
1211
1212 def assert_embedded_icmp_checksum_valid(self, received_packet):
1213 if received_packet.haslayer(IPerror):
1214 self.assert_checksum_valid(received_packet, 'IPerror')
1215 if received_packet.haslayer(TCPerror):
1216 self.assert_checksum_valid(received_packet, 'TCPerror')
1217 if received_packet.haslayer(UDPerror):
1218 self.assert_checksum_valid(received_packet, 'UDPerror',
1219 ignore_zero_checksum=True)
1220 if received_packet.haslayer(ICMPerror):
1221 self.assert_checksum_valid(received_packet, 'ICMPerror')
1222
1223 def assert_icmp_checksum_valid(self, received_packet):
1224 self.assert_checksum_valid(received_packet, 'ICMP')
1225 self.assert_embedded_icmp_checksum_valid(received_packet)
1226
1227 def assert_icmpv6_checksum_valid(self, pkt):
1228 if pkt.haslayer(ICMPv6DestUnreach):
1229 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1230 self.assert_embedded_icmp_checksum_valid(pkt)
1231 if pkt.haslayer(ICMPv6EchoRequest):
1232 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1233 if pkt.haslayer(ICMPv6EchoReply):
1234 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1235
Klement Sekera3a343d42019-05-16 14:35:46 +02001236 def get_packet_counter(self, counter):
1237 if counter.startswith("/"):
1238 counter_value = self.statistics.get_counter(counter)
1239 else:
1240 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001241 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001242 for i in range(1, len(counters) - 1):
1243 results = counters[i].split()
1244 if results[1] == counter:
1245 counter_value = int(results[0])
1246 break
1247 return counter_value
1248
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001249 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001250 counter_value = self.get_packet_counter(counter)
1251 self.assert_equal(counter_value, expected_value,
1252 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001253
Ole Troan233e4682019-05-16 15:01:34 +02001254 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001255 counter_value = self.statistics[counter].sum()
Ole Troan233e4682019-05-16 15:01:34 +02001256 self.assert_equal(counter_value, expected_value,
1257 "error counter `%s'" % counter)
1258
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001259 @classmethod
1260 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001261
1262 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1263 # * by Guido, only the main thread can be interrupted.
1264 # */
1265 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1266 if timeout == 0:
1267 # yield quantum
1268 if hasattr(os, 'sched_yield'):
1269 os.sched_yield()
1270 else:
1271 time.sleep(0)
1272 return
1273
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001274 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001275 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001276 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001277 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001278 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001279 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001280 "slept for %es instead of ~%es!",
1281 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001282
1283 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001284 "Finished sleep (%s) - slept %es (wanted %es)",
1285 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001286
Benoît Ganne56eccdb2021-08-20 09:18:31 +02001287 def virtual_sleep(self, timeout, remark=None):
1288 self.logger.debug("Moving VPP time by %s (%s)", timeout, remark)
1289 self.vapi.cli("set clock adjust %s" % timeout)
1290
Benoît Ganne8c45e512021-02-19 16:39:13 +01001291 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001292 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001293 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001294 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001295
1296 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1297 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001298 if not timeout:
1299 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001300 for i in self.pg_interfaces:
1301 i.get_capture(0, timeout=timeout)
1302 i.assert_nothing_captured(remark=remark)
1303 timeout = 0.1
1304
Benoît Ganne8c45e512021-02-19 16:39:13 +01001305 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1306 trace=True):
Neale Rannsd7603d92019-03-28 08:56:10 +00001307 if not n_rx:
1308 n_rx = len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001309 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001310 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +02001311 if trace:
1312 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001313 return rx
1314
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001315 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1316 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001317 rx = output.get_capture(len(pkts))
1318 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001319 if not timeout:
1320 timeout = 1
1321 for i in self.pg_interfaces:
1322 if i not in outputs:
1323 i.get_capture(0, timeout=timeout)
1324 i.assert_nothing_captured()
1325 timeout = 0.1
1326
Neale Ranns52fae862018-01-08 04:41:42 -08001327 return rx
1328
Damjan Marionf56b77a2016-10-03 19:44:57 +02001329
juraj.linkes184870a2018-07-16 14:22:01 +02001330def get_testcase_doc_name(test):
1331 return getdoc(test.__class__).splitlines()[0]
1332
1333
Ole Trøan5ba91592018-11-22 10:01:09 +00001334def get_test_description(descriptions, test):
1335 short_description = test.shortDescription()
1336 if descriptions and short_description:
1337 return short_description
1338 else:
1339 return str(test)
1340
1341
juraj.linkes40dd73b2018-09-21 13:55:16 +02001342class TestCaseInfo(object):
1343 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1344 self.logger = logger
1345 self.tempdir = tempdir
1346 self.vpp_pid = vpp_pid
1347 self.vpp_bin_path = vpp_bin_path
1348 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001349
1350
Damjan Marionf56b77a2016-10-03 19:44:57 +02001351class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001352 """
1353 @property result_string
1354 String variable to store the test case result string.
1355 @property errors
1356 List variable containing 2-tuples of TestCase instances and strings
1357 holding formatted tracebacks. Each tuple represents a test which
1358 raised an unexpected exception.
1359 @property failures
1360 List variable containing 2-tuples of TestCase instances and strings
1361 holding formatted tracebacks. Each tuple represents a test where
1362 a failure was explicitly signalled using the TestCase.assert*()
1363 methods.
1364 """
1365
juraj.linkes40dd73b2018-09-21 13:55:16 +02001366 failed_test_cases_info = set()
1367 core_crash_test_cases_info = set()
1368 current_test_case_info = None
1369
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001370 def __init__(self, stream=None, descriptions=None, verbosity=None,
1371 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001372 """
Klement Sekerada505f62017-01-04 12:58:53 +01001373 :param stream File descriptor to store where to report test results.
1374 Set to the standard error stream by default.
1375 :param descriptions Boolean variable to store information if to use
1376 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001377 :param verbosity Integer variable to store required verbosity level.
1378 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001379 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001380 self.stream = stream
1381 self.descriptions = descriptions
1382 self.verbosity = verbosity
1383 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001384 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001385 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001386
Damjan Marionf56b77a2016-10-03 19:44:57 +02001387 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001388 """
1389 Record a test succeeded result
1390
1391 :param test:
1392
1393 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001394 if self.current_test_case_info:
1395 self.current_test_case_info.logger.debug(
1396 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1397 test._testMethodName,
1398 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001399 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001400 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001401
juraj.linkescae64f82018-09-19 15:01:47 +02001402 self.send_result_through_pipe(test, PASS)
1403
Klement Sekeraf62ae122016-10-11 11:47:09 +02001404 def addSkip(self, test, reason):
1405 """
1406 Record a test skipped.
1407
1408 :param test:
1409 :param reason:
1410
1411 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001412 if self.current_test_case_info:
1413 self.current_test_case_info.logger.debug(
1414 "--- addSkip() %s.%s(%s) called, reason is %s" %
1415 (test.__class__.__name__, test._testMethodName,
1416 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001417 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001418 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001419
Klement Sekera558ceab2021-04-08 19:37:41 +02001420 if reason == "not enough cpus":
1421 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1422 else:
1423 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001424
juraj.linkes40dd73b2018-09-21 13:55:16 +02001425 def symlink_failed(self):
1426 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001427 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001428 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001429 link_path = os.path.join(
1430 failed_dir,
1431 '%s-FAILED' %
1432 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001433
1434 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001435 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001436 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001437 "os.symlink(%s, %s)" %
1438 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001439 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001440 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001441 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001442 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001443 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001444
Klement Sekeraf413bef2017-08-15 07:09:02 +02001445 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001446 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001447
juraj.linkescae64f82018-09-19 15:01:47 +02001448 def send_result_through_pipe(self, test, result):
1449 if hasattr(self, 'test_framework_result_pipe'):
1450 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001451 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001452 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001453
juraj.linkes40dd73b2018-09-21 13:55:16 +02001454 def log_error(self, test, err, fn_name):
1455 if self.current_test_case_info:
1456 if isinstance(test, unittest.suite._ErrorHolder):
1457 test_name = test.description
1458 else:
1459 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1460 test._testMethodName,
1461 test._testMethodDoc)
1462 self.current_test_case_info.logger.debug(
1463 "--- %s() %s called, err is %s" %
1464 (fn_name, test_name, err))
1465 self.current_test_case_info.logger.debug(
1466 "formatted exception is:\n%s" %
1467 "".join(format_exception(*err)))
1468
1469 def add_error(self, test, err, unittest_fn, error_type):
1470 if error_type == FAIL:
1471 self.log_error(test, err, 'addFailure')
1472 error_type_str = colorize("FAIL", RED)
1473 elif error_type == ERROR:
1474 self.log_error(test, err, 'addError')
1475 error_type_str = colorize("ERROR", RED)
1476 else:
1477 raise Exception('Error type %s cannot be used to record an '
1478 'error or a failure' % error_type)
1479
1480 unittest_fn(self, test, err)
1481 if self.current_test_case_info:
1482 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1483 (error_type_str,
1484 self.current_test_case_info.tempdir)
1485 self.symlink_failed()
1486 self.failed_test_cases_info.add(self.current_test_case_info)
1487 if is_core_present(self.current_test_case_info.tempdir):
1488 if not self.current_test_case_info.core_crash_test:
1489 if isinstance(test, unittest.suite._ErrorHolder):
1490 test_name = str(test)
1491 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001492 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001493 get_testcase_doc_name(test), test.id())
1494 self.current_test_case_info.core_crash_test = test_name
1495 self.core_crash_test_cases_info.add(
1496 self.current_test_case_info)
1497 else:
1498 self.result_string = '%s [no temp dir]' % error_type_str
1499
1500 self.send_result_through_pipe(test, error_type)
1501
Damjan Marionf56b77a2016-10-03 19:44:57 +02001502 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001503 """
1504 Record a test failed result
1505
1506 :param test:
1507 :param err: error message
1508
1509 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001510 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001511
Damjan Marionf56b77a2016-10-03 19:44:57 +02001512 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001513 """
1514 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001515
Klement Sekeraf62ae122016-10-11 11:47:09 +02001516 :param test:
1517 :param err: error message
1518
1519 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001520 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001521
Damjan Marionf56b77a2016-10-03 19:44:57 +02001522 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001523 """
1524 Get test description
1525
1526 :param test:
1527 :returns: test description
1528
1529 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001530 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001531
Damjan Marionf56b77a2016-10-03 19:44:57 +02001532 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001533 """
1534 Start a test
1535
1536 :param test:
1537
1538 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001539
1540 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001541 if test.__class__ in self.printed:
1542 return
1543
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001544 test_doc = getdoc(test)
1545 if not test_doc:
1546 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001547
Klement Sekeraea6236b2021-03-25 14:03:44 +01001548 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001549 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001550 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001551 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001552
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001553 # This block may overwrite the colorized title above,
1554 # but we want this to stand out and be fixed
1555 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekera558ceab2021-04-08 19:37:41 +02001556 test_title = colorize(
1557 f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001558
Klement Sekeraea6236b2021-03-25 14:03:44 +01001559 if hasattr(test, 'vpp_worker_count'):
1560 if test.vpp_worker_count == 0:
1561 test_title += " [main thread only]"
1562 elif test.vpp_worker_count == 1:
1563 test_title += " [1 worker thread]"
1564 else:
1565 test_title += f" [{test.vpp_worker_count} worker threads]"
1566
Klement Sekera558ceab2021-04-08 19:37:41 +02001567 if test.__class__.skipped_due_to_cpu_lack:
1568 test_title = colorize(
1569 f"{test_title} [skipped - not enough cpus, "
1570 f"required={test.__class__.get_cpus_required()}, "
1571 f"available={max_vpp_cpus}]", YELLOW)
1572
1573 print(double_line_delim)
1574 print(test_title)
1575 print(double_line_delim)
1576 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001577
1578 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001579 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001580 unittest.TestResult.startTest(self, test)
1581 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001582 self.stream.writeln(
1583 "Starting " + self.getDescription(test) + " ...")
1584 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001585
Damjan Marionf56b77a2016-10-03 19:44:57 +02001586 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001587 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001588 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001589
1590 :param test:
1591
1592 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001593 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001594
Damjan Marionf56b77a2016-10-03 19:44:57 +02001595 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001596 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001597 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001598 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001599 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001600 else:
Ole Troan0c629322019-11-28 14:48:44 +01001601 self.stream.writeln("%-68s %4.2f %s" %
1602 (self.getDescription(test),
1603 time.time() - self.start_test,
1604 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001605
1606 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001607
Damjan Marionf56b77a2016-10-03 19:44:57 +02001608 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001609 """
1610 Print errors from running the test case
1611 """
juraj.linkesabec0122018-11-16 17:28:56 +01001612 if len(self.errors) > 0 or len(self.failures) > 0:
1613 self.stream.writeln()
1614 self.printErrorList('ERROR', self.errors)
1615 self.printErrorList('FAIL', self.failures)
1616
1617 # ^^ that is the last output from unittest before summary
1618 if not self.runner.print_summary:
1619 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1620 self.stream = devnull
1621 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001622
Damjan Marionf56b77a2016-10-03 19:44:57 +02001623 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001624 """
1625 Print error list to the output stream together with error type
1626 and test case description.
1627
1628 :param flavour: error type
1629 :param errors: iterable errors
1630
1631 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001632 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001633 self.stream.writeln(double_line_delim)
1634 self.stream.writeln("%s: %s" %
1635 (flavour, self.getDescription(test)))
1636 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001637 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001638
1639
Damjan Marionf56b77a2016-10-03 19:44:57 +02001640class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001641 """
Klement Sekera104543f2017-02-03 07:29:43 +01001642 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001643 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001644
Klement Sekeraf62ae122016-10-11 11:47:09 +02001645 @property
1646 def resultclass(self):
1647 """Class maintaining the results of the tests"""
1648 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001649
juraj.linkes184870a2018-07-16 14:22:01 +02001650 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001651 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001652 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001653 # ignore stream setting here, use hard-coded stdout to be in sync
1654 # with prints from VppTestCase methods ...
1655 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1656 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001657 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001658 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001659
juraj.linkesabec0122018-11-16 17:28:56 +01001660 self.orig_stream = self.stream
1661 self.resultclass.test_framework_result_pipe = result_pipe
1662
1663 self.print_summary = print_summary
1664
1665 def _makeResult(self):
1666 return self.resultclass(self.stream,
1667 self.descriptions,
1668 self.verbosity,
1669 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001670
Damjan Marionf56b77a2016-10-03 19:44:57 +02001671 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001672 """
1673 Run the tests
1674
1675 :param test:
1676
1677 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001678 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001679
1680 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001681 if not self.print_summary:
1682 self.stream = self.orig_stream
1683 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001684 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001685
1686
1687class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001688 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1689 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001690 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001691 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001692 if hasattr(self, 'testcase') and self.testcase.debug_all:
1693 if self.testcase.debug_gdbserver:
1694 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1695 .format(port=self.testcase.gdbserver_port)] + args
1696 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1697 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001698 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001699 self.app_name = os.path.basename(self.app_bin)
1700 if hasattr(self, 'role'):
1701 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001702 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001703 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001704 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001705 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001706
Dave Wallace24564332019-10-21 02:53:14 +00001707 def wait_for_enter(self):
1708 if not hasattr(self, 'testcase'):
1709 return
1710 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1711 print()
1712 print(double_line_delim)
1713 print("Spawned GDB Server for '{app}' with PID: {pid}"
1714 .format(app=self.app_name, pid=self.process.pid))
1715 elif self.testcase.debug_all and self.testcase.debug_gdb:
1716 print()
1717 print(double_line_delim)
1718 print("Spawned '{app}' with PID: {pid}"
1719 .format(app=self.app_name, pid=self.process.pid))
1720 else:
1721 return
1722 print(single_line_delim)
1723 print("You can debug '{app}' using:".format(app=self.app_name))
1724 if self.testcase.debug_gdbserver:
1725 print("sudo gdb " + self.app_bin +
1726 " -ex 'target remote localhost:{port}'"
1727 .format(port=self.testcase.gdbserver_port))
1728 print("Now is the time to attach gdb by running the above "
1729 "command, set up breakpoints etc., then resume from "
1730 "within gdb by issuing the 'continue' command")
1731 self.testcase.gdbserver_port += 1
1732 elif self.testcase.debug_gdb:
1733 print("sudo gdb " + self.app_bin +
1734 " -ex 'attach {pid}'".format(pid=self.process.pid))
1735 print("Now is the time to attach gdb by running the above "
1736 "command and set up breakpoints etc., then resume from"
1737 " within gdb by issuing the 'continue' command")
1738 print(single_line_delim)
1739 input("Press ENTER to continue running the testcase...")
1740
Neale Ranns812ed392017-10-16 04:20:13 -07001741 def run(self):
1742 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001743 if not os.path.exists(executable) or not os.access(
1744 executable, os.F_OK | os.X_OK):
1745 # Exit code that means some system file did not exist,
1746 # could not be opened, or had some other kind of error.
1747 self.result = os.EX_OSFILE
1748 raise EnvironmentError(
1749 "executable '%s' is not found or executable." % executable)
Dave Wallace4ee17d82021-05-20 14:01:51 -04001750 self.logger.debug("Running executable '{app}': '{cmd}'"
1751 .format(app=self.app_name,
1752 cmd=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001753 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001754 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001755 env["CK_LOG_FILE_NAME"] = "-"
1756 self.process = subprocess.Popen(
Dave Wallaceae8d3322021-05-18 17:12:16 -04001757 ['stdbuf', '-o0', '-e0'] + self.args, shell=False, env=env,
1758 preexec_fn=os.setpgrp, stdout=subprocess.PIPE,
1759 stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001760 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001761 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001762 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001763 self.logger.info("Return code is `%s'" % self.process.returncode)
1764 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001765 self.logger.info("Executable `{app}' wrote to stdout:"
1766 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001767 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001768 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001769 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001770 self.logger.info("Executable `{app}' wrote to stderr:"
1771 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001772 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001773 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001774 self.logger.info(single_line_delim)
1775 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001776
Klement Sekera6aa58b72019-05-16 14:34:55 +02001777
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001778if __name__ == '__main__':
1779 pass