blob: c2a2fc6843059f7c11e81b7e4587684facfc1017 [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
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070026
27import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000028from scapy.packet import Raw
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040029import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080030from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010031from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000032from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070033from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000034from vpp_papi_provider import VppPapiProvider
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050035import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000036from vpp_papi.vpp_stats import VPPStats
Ole Troan4376ab22021-03-03 10:40:05 +010037from vpp_papi.vpp_transport_socket import VppTransportSocketIOError
Ole Trøan162989e2018-11-26 10:27:50 +000038from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
39 get_logger, colorize
40from vpp_object import VppObjectRegistry
41from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020042from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
43from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
44from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080045
Klement Sekera558ceab2021-04-08 19:37:41 +020046from cpu_config import available_cpus, num_cpus, max_vpp_cpus
47
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050048logger = logging.getLogger(__name__)
49
50# Set up an empty logger for the testcase that can be overridden as necessary
51null_logger = logging.getLogger('VppTestCase')
52null_logger.addHandler(logging.NullHandler())
53
juraj.linkescae64f82018-09-19 15:01:47 +020054PASS = 0
55FAIL = 1
56ERROR = 2
57SKIP = 3
58TEST_RUN = 4
Klement Sekera558ceab2021-04-08 19:37:41 +020059SKIP_CPU_SHORTAGE = 5
juraj.linkescae64f82018-09-19 15:01:47 +020060
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040061
62class BoolEnvironmentVariable(object):
63
64 def __init__(self, env_var_name, default='n', true_values=None):
65 self.name = env_var_name
66 self.default = default
67 self.true_values = true_values if true_values is not None else \
68 ("y", "yes", "1")
69
70 def __bool__(self):
71 return os.getenv(self.name, self.default).lower() in self.true_values
72
73 if sys.version_info[0] == 2:
74 __nonzero__ = __bool__
75
76 def __repr__(self):
77 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
78 (self.name, self.default, self.true_values)
79
80
81debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
82if debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010083 import debug_internal
84
Klement Sekeraf62ae122016-10-11 11:47:09 +020085"""
86 Test framework module.
87
88 The module provides a set of tools for constructing and running tests and
89 representing the results.
90"""
91
Klement Sekeraf62ae122016-10-11 11:47:09 +020092
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040093class VppDiedError(Exception):
94 """ exception for reporting that the subprocess has died."""
95
96 signals_by_value = {v: k for k, v in signal.__dict__.items() if
97 k.startswith('SIG') and not k.startswith('SIG_')}
98
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040099 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400100 self.rv = rv
101 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400102 self.testcase = testcase
103 self.method_name = method_name
104
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400105 try:
106 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400107 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400108 pass
109
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400110 if testcase is None and method_name is None:
111 in_msg = ''
112 else:
Klement Sekera79a31db2021-03-12 18:16:10 +0100113 in_msg = ' while running %s.%s' % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400114
Klement Sekera79a31db2021-03-12 18:16:10 +0100115 if self.rv:
116 msg = "VPP subprocess died unexpectedly%s with return code: %d%s."\
117 % (in_msg, self.rv, ' [%s]' %
118 (self.signal_name if
119 self.signal_name is not None else ''))
120 else:
121 msg = "VPP subprocess died unexpectedly%s." % in_msg
122
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400123 super(VppDiedError, self).__init__(msg)
124
125
Damjan Marionf56b77a2016-10-03 19:44:57 +0200126class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200127 """Private class to create packet info object.
128
129 Help process information about the next packet.
130 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200131 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100132 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200133 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100134 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200135 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100136 #: Store the index of the destination packet generator interface
137 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200138 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100139 #: Store expected ip version
140 ip = -1
141 #: Store expected upper protocol
142 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100143 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200144 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200145
Matej Klotton16a14cd2016-12-07 15:09:13 +0100146 def __eq__(self, other):
147 index = self.index == other.index
148 src = self.src == other.src
149 dst = self.dst == other.dst
150 data = self.data == other.data
151 return index and src and dst and data
152
Klement Sekeraf62ae122016-10-11 11:47:09 +0200153
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100154def pump_output(testclass):
155 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100156 stdout_fragment = ""
157 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400158 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100159 readable = select.select([testclass.vpp.stdout.fileno(),
160 testclass.vpp.stderr.fileno(),
161 testclass.pump_thread_wakeup_pipe[0]],
162 [], [])[0]
163 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100164 read = os.read(testclass.vpp.stdout.fileno(), 102400)
165 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200166 split = read.decode('ascii',
167 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100168 if len(stdout_fragment) > 0:
169 split[0] = "%s%s" % (stdout_fragment, split[0])
170 if len(split) > 0 and split[-1].endswith("\n"):
171 limit = None
172 else:
173 limit = -1
174 stdout_fragment = split[-1]
175 testclass.vpp_stdout_deque.extend(split[:limit])
176 if not testclass.cache_vpp_output:
177 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100178 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100179 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100180 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100181 read = os.read(testclass.vpp.stderr.fileno(), 102400)
182 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200183 split = read.decode('ascii',
184 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100185 if len(stderr_fragment) > 0:
186 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200187 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100188 limit = None
189 else:
190 limit = -1
191 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200192
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100193 testclass.vpp_stderr_deque.extend(split[:limit])
194 if not testclass.cache_vpp_output:
195 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100196 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100197 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800198 # ignoring the dummy pipe here intentionally - the
199 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200200
201
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800202def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400203 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100204
Klement Sekera6aa58b72019-05-16 14:34:55 +0200205
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800206is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100207
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800208
209def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100210 return platform.machine() == 'aarch64'
211
Klement Sekera6aa58b72019-05-16 14:34:55 +0200212
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800213is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100214
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800215
216def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400217 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100218
Klement Sekera6aa58b72019-05-16 14:34:55 +0200219
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800220running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100221
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800222
Dave Barachd498c9e2020-03-10 16:59:39 -0400223def _running_gcov_tests():
224 return BoolEnvironmentVariable("GCOV_TESTS")
225
226
227running_gcov_tests = _running_gcov_tests()
228
229
Klement Sekera558ceab2021-04-08 19:37:41 +0200230def get_environ_vpp_worker_count():
231 worker_config = os.getenv("VPP_WORKER_CONFIG", None)
232 if worker_config:
233 elems = worker_config.split(" ")
234 if elems[0] != "workers" or len(elems) != 2:
235 raise ValueError("Wrong VPP_WORKER_CONFIG == '%s' value." %
236 worker_config)
237 return int(elems[1])
238 else:
239 return 0
240
241
242environ_vpp_worker_count = get_environ_vpp_worker_count()
243
244
Klement Sekera909a6a12017-08-08 04:33:53 +0200245class KeepAliveReporter(object):
246 """
247 Singleton object which reports test start to parent process
248 """
249 _shared_state = {}
250
251 def __init__(self):
252 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800253 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200254
255 @property
256 def pipe(self):
257 return self._pipe
258
259 @pipe.setter
260 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800261 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200262 raise Exception("Internal error - pipe should only be set once.")
263 self._pipe = pipe
264
juraj.linkes40dd73b2018-09-21 13:55:16 +0200265 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200266 """
267 Write current test tmpdir & desc to keep-alive pipe to signal liveness
268 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200269 if self.pipe is None:
270 # if not running forked..
271 return
272
Klement Sekera909a6a12017-08-08 04:33:53 +0200273 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200274 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200275 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200276 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200277
Dave Wallacee2efd122017-09-30 22:04:21 -0400278 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200279
280
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000281class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000282 # marks the suites that must run at the end
283 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000284 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000285 # marks the suites broken on VPP multi-worker
286 FIXME_VPP_WORKERS = 2
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000287
288
289def create_tag_decorator(e):
290 def decorator(cls):
291 try:
292 cls.test_tags.append(e)
293 except AttributeError:
294 cls.test_tags = [e]
295 return cls
296 return decorator
297
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000298
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000299tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000300tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000301
302
Klement Sekerae2636852021-03-16 12:52:12 +0100303class DummyVpp:
304 returncode = None
305 pid = 0xcafebafe
306
307 def poll(self):
308 pass
309
310 def terminate(self):
311 pass
312
313
Klement Sekera558ceab2021-04-08 19:37:41 +0200314class CPUInterface(ABC):
315 cpus = []
316 skipped_due_to_cpu_lack = False
317
318 @classmethod
319 @abstractmethod
320 def get_cpus_required(cls):
321 pass
322
323 @classmethod
324 def assign_cpus(cls, cpus):
325 cls.cpus = cpus
326
327
328class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100329 """This subclass is a base class for VPP test cases that are implemented as
330 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200331 """
332
Arthur de Kerhordb023802021-03-11 10:26:54 -0800333 extra_vpp_statseg_config = ""
Ole Troana45dc072018-12-21 16:04:22 +0100334 extra_vpp_punt_config = []
335 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500336 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400337 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100338
Klement Sekeraf62ae122016-10-11 11:47:09 +0200339 @property
340 def packet_infos(self):
341 """List of packet infos"""
342 return self._packet_infos
343
Klement Sekeradab231a2016-12-21 08:50:14 +0100344 @classmethod
345 def get_packet_count_for_if_idx(cls, dst_if_index):
346 """Get the number of packet info for specified destination if index"""
347 if dst_if_index in cls._packet_count_for_dst_if_idx:
348 return cls._packet_count_for_dst_if_idx[dst_if_index]
349 else:
350 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200351
352 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000353 def has_tag(cls, tag):
354 """ if the test case has a given tag - return true """
355 try:
356 return tag in cls.test_tags
357 except AttributeError:
358 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000359 return False
360
361 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000362 def is_tagged_run_solo(cls):
363 """ if the test case class is timing-sensitive - return true """
364 return cls.has_tag(TestCaseTag.RUN_SOLO)
365
366 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200367 def instance(cls):
368 """Return the instance of this testcase"""
369 return cls.test_instance
370
Damjan Marionf56b77a2016-10-03 19:44:57 +0200371 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200372 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000373 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200374 cls.debug_core = False
375 cls.debug_gdb = False
376 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000377 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100378 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200379 if d is None:
380 return
381 dl = d.lower()
382 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200383 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000384 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200385 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000386 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200387 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100388 elif dl == "attach":
389 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200390 else:
391 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000392 if dl == "gdb-all" or dl == "gdbserver-all":
393 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200394
Klement Sekera558ceab2021-04-08 19:37:41 +0200395 @classmethod
396 def get_vpp_worker_count(cls):
397 if not hasattr(cls, "vpp_worker_count"):
398 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
399 cls.vpp_worker_count = 0
400 else:
401 cls.vpp_worker_count = environ_vpp_worker_count
402 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200403
Klement Sekera558ceab2021-04-08 19:37:41 +0200404 @classmethod
405 def get_cpus_required(cls):
406 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200407
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800408 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200409 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200410 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400411 cls.step = BoolEnvironmentVariable('STEP')
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400412 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100413 c = os.getenv("CACHE_OUTPUT", "1")
414 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekerab8c72a42018-11-08 11:21:39 +0100415 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
416 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400417 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100418 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
419 plugin_path = None
420 if cls.plugin_path is not None:
421 if cls.extern_plugin_path is not None:
422 plugin_path = "%s:%s" % (
423 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100424 else:
425 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100426 elif cls.extern_plugin_path is not None:
427 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100428 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100429 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100430 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100431 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100432 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100433 if size is not None:
434 coredump_size = "coredump-size %s" % size
435 if coredump_size is None:
436 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200437
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000438 default_variant = os.getenv("VARIANT")
439 if default_variant is not None:
440 default_variant = "defaults { %s 100 }" % default_variant
441 else:
442 default_variant = ""
443
Dave Barach77841402020-04-29 17:04:10 -0400444 api_fuzzing = os.getenv("API_FUZZ")
445 if api_fuzzing is None:
446 api_fuzzing = 'off'
447
Klement Sekera8d815022021-03-15 16:58:10 +0100448 cls.vpp_cmdline = [
449 cls.vpp_bin,
450 "unix", "{", "nodaemon", debug_cli, "full-coredump",
451 coredump_size, "runtime-dir", cls.tempdir, "}",
452 "api-trace", "{", "on", "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100453 "api-segment", "{", "prefix", cls.get_api_segment_prefix(), "}",
Klement Sekera558ceab2021-04-08 19:37:41 +0200454 "cpu", "{", "main-core", str(cls.cpus[0]), ]
455 if cls.get_vpp_worker_count():
456 cls.vpp_cmdline.extend([
457 "corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])])
Klement Sekera8d815022021-03-15 16:58:10 +0100458 cls.vpp_cmdline.extend([
459 "}",
460 "physmem", "{", "max-size", "32m", "}",
Arthur de Kerhordb023802021-03-11 10:26:54 -0800461 "statseg", "{", "socket-name", cls.get_stats_sock_path(),
462 cls.extra_vpp_statseg_config, "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100463 "socksvr", "{", "socket-name", cls.get_api_sock_path(), "}",
Klement Sekera8d815022021-03-15 16:58:10 +0100464 "node { ", default_variant, "}",
465 "api-fuzz {", api_fuzzing, "}",
466 "plugins", "{", "plugin", "dpdk_plugin.so", "{", "disable", "}",
467 "plugin", "rdma_plugin.so", "{", "disable", "}",
468 "plugin", "lisp_unittest_plugin.so", "{", "enable", "}",
469 "plugin", "unittest_plugin.so", "{", "enable", "}"
470 ] + cls.extra_vpp_plugin_config + ["}", ])
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000471
Ole Troana45dc072018-12-21 16:04:22 +0100472 if cls.extra_vpp_punt_config is not None:
473 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100474 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100475 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400476 if cls.test_plugin_path is not None:
477 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
478
Klement Sekerae2636852021-03-16 12:52:12 +0100479 if not cls.debug_attach:
480 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
481 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200482
483 @classmethod
484 def wait_for_enter(cls):
485 if cls.debug_gdbserver:
486 print(double_line_delim)
487 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
488 elif cls.debug_gdb:
489 print(double_line_delim)
490 print("Spawned VPP with PID: %d" % cls.vpp.pid)
491 else:
492 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
493 return
494 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000495 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200496 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400497 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000498 " -ex 'target remote localhost:{port}'"
499 .format(port=cls.gdbserver_port))
500 print("Now is the time to attach gdb by running the above "
501 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200502 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000503 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200504 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400505 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000506 print("Now is the time to attach gdb by running the above "
507 "command and set up breakpoints etc., then resume VPP from"
508 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200509 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800510 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200511
512 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100513 def attach_vpp(cls):
514 cls.vpp = DummyVpp()
515
516 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200517 def run_vpp(cls):
Klement Sekera558ceab2021-04-08 19:37:41 +0200518 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200519 cmdline = cls.vpp_cmdline
520
521 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100522 gdbserver = '/usr/bin/gdbserver'
Klement Sekera558ceab2021-04-08 19:37:41 +0200523 if not os.path.isfile(gdbserver) or\
Klement Sekera931be3a2016-11-03 05:36:01 +0100524 not os.access(gdbserver, os.X_OK):
525 raise Exception("gdbserver binary '%s' does not exist or is "
526 "not executable" % gdbserver)
527
Dave Wallace24564332019-10-21 02:53:14 +0000528 cmdline = [gdbserver, 'localhost:{port}'
529 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200530 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
531
Klement Sekera931be3a2016-11-03 05:36:01 +0100532 try:
533 cls.vpp = subprocess.Popen(cmdline,
534 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100535 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800536 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800537 cls.logger.critical("Subprocess returned with non-0 return code: ("
538 "%s)", e.returncode)
539 raise
540 except OSError as e:
541 cls.logger.critical("Subprocess returned with OS error: "
542 "(%s) %s", e.errno, e.strerror)
543 raise
544 except Exception as e:
545 cls.logger.exception("Subprocess returned unexpected from "
546 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100547 raise
548
Klement Sekera277b89c2016-10-28 13:20:27 +0200549 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100550
Damjan Marionf56b77a2016-10-03 19:44:57 +0200551 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000552 def wait_for_coredump(cls):
553 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400554 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000555 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400556 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000557 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400558 ok = False
559 while time.time() < deadline:
560 cls.sleep(1)
561 size = curr_size
562 curr_size = os.path.getsize(corefile)
563 if size == curr_size:
564 ok = True
565 break
566 if not ok:
567 cls.logger.error("Timed out waiting for coredump to complete:"
568 " %s", corefile)
569 else:
570 cls.logger.error("Coredump complete: %s, size %d",
571 corefile, curr_size)
572
573 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100574 def get_stats_sock_path(cls):
575 return "%s/stats.sock" % cls.tempdir
576
577 @classmethod
578 def get_api_sock_path(cls):
579 return "%s/api.sock" % cls.tempdir
580
581 @classmethod
582 def get_api_segment_prefix(cls):
583 return os.path.basename(cls.tempdir) # Only used for VAPI
584
585 @classmethod
586 def get_tempdir(cls):
587 if cls.debug_attach:
588 return os.getenv("VPP_IN_GDB_TMP_DIR",
589 "/tmp/unittest-attach-gdb")
590 else:
591 return tempfile.mkdtemp(prefix='vpp-unittest-%s-' % cls.__name__)
592
593 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200594 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200595 """
596 Perform class setup before running the testcase
597 Remove shared memory files, start vpp and connect the vpp-api
598 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800599 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100600 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000601 seed = os.environ["RND_SEED"]
602 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100603 if hasattr(cls, 'parallel_handler'):
604 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100605 cls.logger.propagate = False
Klement Sekerae2636852021-03-16 12:52:12 +0100606 d = os.getenv("DEBUG", None)
607 cls.set_debug_flags(d)
608 cls.tempdir = cls.get_tempdir()
Klement Sekera027dbd52017-04-11 06:01:53 +0200609 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
610 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100611 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
612 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200613 cls.file_handler.setLevel(DEBUG)
614 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100615 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200616 os.chdir(cls.tempdir)
Ole Troan4376ab22021-03-03 10:40:05 +0100617 cls.logger.info("Temporary dir is %s, api socket is %s",
Klement Sekerae2636852021-03-16 12:52:12 +0100618 cls.tempdir, cls.get_api_sock_path())
619 cls.logger.debug("Random seed is %s", seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200620 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100621 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200622 cls._pcaps = []
623 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200624 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100625 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100626 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200627 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200628 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200629 # need to catch exceptions here because if we raise, then the cleanup
630 # doesn't get called and we might end with a zombie vpp
631 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100632 if cls.debug_attach:
633 cls.attach_vpp()
634 else:
635 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200636 cls.reporter.send_keep_alive(cls, 'setUpClass')
637 VppTestResult.current_test_case_info = TestCaseInfo(
638 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100639 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100640 cls.vpp_stderr_deque = deque()
Klement Sekerae2636852021-03-16 12:52:12 +0100641 if not cls.debug_attach:
642 cls.pump_thread_stop_flag = Event()
643 cls.pump_thread_wakeup_pipe = os.pipe()
644 cls.pump_thread = Thread(target=pump_output, args=(cls,))
645 cls.pump_thread.daemon = True
646 cls.pump_thread.start()
647 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400648 cls.vapi_response_timeout = 0
Ole Troan4376ab22021-03-03 10:40:05 +0100649 cls.vapi = VppPapiProvider(cls.__name__, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400650 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100651 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400652 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100653 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400654 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100655 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100656 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200657 try:
658 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100659 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200660 cls.vpp_startup_failed = True
661 cls.logger.critical(
662 "VPP died shortly after startup, check the"
663 " output to standard error for possible cause")
664 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100665 try:
666 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100667 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500668 cls.logger.debug("Exception connecting to vapi: %s" % e)
669 cls.vapi.disconnect()
670
Klement Sekera085f5c02016-11-24 01:59:16 +0100671 if cls.debug_gdbserver:
672 print(colorize("You're running VPP inside gdbserver but "
673 "VPP-API connection failed, did you forget "
674 "to 'continue' VPP from within gdb?", RED))
Ole Troan4376ab22021-03-03 10:40:05 +0100675 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100676 if cls.debug_attach:
677 last_line = cls.vapi.cli("show thread").split("\n")[-2]
678 cls.vpp_worker_count = int(last_line.split(" ")[0])
679 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400680 except vpp_papi.VPPRuntimeError as e:
681 cls.logger.debug("%s" % e)
682 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100683 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000684 except Exception as e:
685 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400686 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100687 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200688
Damjan Marionf56b77a2016-10-03 19:44:57 +0200689 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500690 def _debug_quit(cls):
691 if (cls.debug_gdbserver or cls.debug_gdb):
692 try:
693 cls.vpp.poll()
694
695 if cls.vpp.returncode is None:
696 print()
697 print(double_line_delim)
698 print("VPP or GDB server is still running")
699 print(single_line_delim)
700 input("When done debugging, press ENTER to kill the "
701 "process and finish running the testcase...")
702 except AttributeError:
703 pass
704
705 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200706 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200707 """
708 Disconnect vpp-api, kill vpp and cleanup shared memory files
709 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500710 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200711
juraj.linkes184870a2018-07-16 14:22:01 +0200712 # first signal that we want to stop the pump thread, then wake it up
713 if hasattr(cls, 'pump_thread_stop_flag'):
714 cls.pump_thread_stop_flag.set()
715 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100716 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100717 if hasattr(cls, 'pump_thread'):
718 cls.logger.debug("Waiting for pump thread to stop")
719 cls.pump_thread.join()
720 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500721 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100722 cls.vpp_stderr_reader_thread.join()
723
Klement Sekeraf62ae122016-10-11 11:47:09 +0200724 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100725 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100726 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700727 cls.logger.debug("Disconnecting class vapi client on %s",
728 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100729 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700730 cls.logger.debug("Deleting class vapi attribute on %s",
731 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100732 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200733 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100734 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000735 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100736 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400737 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100738 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100739 try:
740 outs, errs = cls.vpp.communicate(timeout=5)
741 except subprocess.TimeoutExpired:
742 cls.vpp.kill()
743 outs, errs = cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700744 cls.logger.debug("Deleting class vpp attribute on %s",
745 cls.__name__)
Klement Sekerae2636852021-03-16 12:52:12 +0100746 if not cls.debug_attach:
747 cls.vpp.stdout.close()
748 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200749 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200750
Klement Sekera3747c752017-04-10 06:30:17 +0200751 if cls.vpp_startup_failed:
752 stdout_log = cls.logger.info
753 stderr_log = cls.logger.critical
754 else:
755 stdout_log = cls.logger.info
756 stderr_log = cls.logger.info
757
Klement Sekerae4504c62016-12-08 10:16:41 +0100758 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200759 stdout_log(single_line_delim)
760 stdout_log('VPP output to stdout while running %s:', cls.__name__)
761 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100762 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200763 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
764 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200765 stdout_log('\n%s', vpp_output)
766 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200767
Klement Sekerae4504c62016-12-08 10:16:41 +0100768 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200769 stderr_log(single_line_delim)
770 stderr_log('VPP output to stderr while running %s:', cls.__name__)
771 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100772 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200773 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
774 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200775 stderr_log('\n%s', vpp_output)
776 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200777
Damjan Marionf56b77a2016-10-03 19:44:57 +0200778 @classmethod
779 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200780 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700781 cls.logger.debug("--- tearDownClass() for %s called ---" %
782 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200783 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200785 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100786 cls.reset_packet_infos()
787 if debug_framework:
788 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200789
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700790 def show_commands_at_teardown(self):
791 """ Allow subclass specific teardown logging additions."""
792 self.logger.info("--- No test specific show commands provided. ---")
793
Damjan Marionf56b77a2016-10-03 19:44:57 +0200794 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200795 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100796 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
797 (self.__class__.__name__, self._testMethodName,
798 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700799
800 try:
801 if not self.vpp_dead:
802 self.logger.debug(self.vapi.cli("show trace max 1000"))
803 self.logger.info(self.vapi.ppcli("show interface"))
804 self.logger.info(self.vapi.ppcli("show hardware"))
805 self.logger.info(self.statistics.set_errors_str())
806 self.logger.info(self.vapi.ppcli("show run"))
807 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400808 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700809 self.logger.info("Logging testcase specific show commands.")
810 self.show_commands_at_teardown()
811 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500812 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000813 m = self._testMethodName
814 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500815 tmp_api_trace = "/tmp/%s" % api_trace
816 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
817 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
818 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
819 vpp_api_trace_log))
820 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500821 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500822 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
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200918 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200919 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100920 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200921
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100922 :param interfaces: iterable indexes of the interfaces.
923 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200924
Klement Sekeraf62ae122016-10-11 11:47:09 +0200925 """
926 result = []
927 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200928 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200929 setattr(cls, intf.name, intf)
930 result.append(intf)
931 cls.pg_interfaces = result
932 return result
933
Matej Klotton0178d522016-11-04 11:11:44 +0100934 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200935 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100936 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100937 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100938
Klement Sekerab9ef2732018-06-24 22:49:33 +0200939 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100940 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100941 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200942 result = [VppLoInterface(cls) for i in range(count)]
943 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100944 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100945 cls.lo_interfaces = result
946 return result
947
Neale Ranns192b13f2019-03-15 02:16:20 -0700948 @classmethod
949 def create_bvi_interfaces(cls, count):
950 """
951 Create BVI interfaces.
952
953 :param count: number of interfaces created.
954 :returns: List of created interfaces.
955 """
956 result = [VppBviInterface(cls) for i in range(count)]
957 for intf in result:
958 setattr(cls, intf.name, intf)
959 cls.bvi_interfaces = result
960 return result
961
Damjan Marionf56b77a2016-10-03 19:44:57 +0200962 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200963 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200964 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200965 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200966 NOTE: Currently works only when Raw layer is present.
967
968 :param packet: packet
969 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200970 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200971
972 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200973 packet_len = len(packet) + 4
974 extend = size - packet_len
975 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200976 num = (extend // len(padding)) + 1
977 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200978
Klement Sekeradab231a2016-12-21 08:50:14 +0100979 @classmethod
980 def reset_packet_infos(cls):
981 """ Reset the list of packet info objects and packet counts to zero """
982 cls._packet_infos = {}
983 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200984
Klement Sekeradab231a2016-12-21 08:50:14 +0100985 @classmethod
986 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200987 """
988 Create packet info object containing the source and destination indexes
989 and add it to the testcase's packet info list
990
Klement Sekeradab231a2016-12-21 08:50:14 +0100991 :param VppInterface src_if: source interface
992 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200993
994 :returns: _PacketInfo object
995
996 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200997 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100998 info.index = len(cls._packet_infos)
999 info.src = src_if.sw_if_index
1000 info.dst = dst_if.sw_if_index
1001 if isinstance(dst_if, VppSubInterface):
1002 dst_idx = dst_if.parent.sw_if_index
1003 else:
1004 dst_idx = dst_if.sw_if_index
1005 if dst_idx in cls._packet_count_for_dst_if_idx:
1006 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1007 else:
1008 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1009 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001010 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001011
Damjan Marionf56b77a2016-10-03 19:44:57 +02001012 @staticmethod
1013 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001014 """
1015 Convert _PacketInfo object to packet payload
1016
1017 :param info: _PacketInfo object
1018
1019 :returns: string containing serialized data from packet info
1020 """
Pavel Kotucek59dda062017-03-02 15:22:47 +01001021 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
1022 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001023
Damjan Marionf56b77a2016-10-03 19:44:57 +02001024 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001025 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001026 """
1027 Convert packet payload to _PacketInfo object
1028
1029 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001030 :type payload: <class 'scapy.packet.Raw'>
1031 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001032 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001033 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001034 :returns: _PacketInfo object containing de-serialized data from payload
1035
1036 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001037 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001038 info = _PacketInfo()
1039 info.index = int(numbers[0])
1040 info.src = int(numbers[1])
1041 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +01001042 info.ip = int(numbers[3])
1043 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +02001044 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001045
Damjan Marionf56b77a2016-10-03 19:44:57 +02001046 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001047 """
1048 Iterate over the packet info list stored in the testcase
1049 Start iteration with first element if info is None
1050 Continue based on index in info if info is specified
1051
1052 :param info: info or None
1053 :returns: next info in list or None if no more infos
1054 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001055 if info is None:
1056 next_index = 0
1057 else:
1058 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001059 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001060 return None
1061 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001062 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001063
Klement Sekeraf62ae122016-10-11 11:47:09 +02001064 def get_next_packet_info_for_interface(self, src_index, info):
1065 """
1066 Search the packet info list for the next packet info with same source
1067 interface index
1068
1069 :param src_index: source interface index to search for
1070 :param info: packet info - where to start the search
1071 :returns: packet info or None
1072
1073 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001074 while True:
1075 info = self.get_next_packet_info(info)
1076 if info is None:
1077 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001078 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001079 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001080
Klement Sekeraf62ae122016-10-11 11:47:09 +02001081 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1082 """
1083 Search the packet info list for the next packet info with same source
1084 and destination interface indexes
1085
1086 :param src_index: source interface index to search for
1087 :param dst_index: destination interface index to search for
1088 :param info: packet info - where to start the search
1089 :returns: packet info or None
1090
1091 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001092 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001093 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001094 if info is None:
1095 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001096 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001097 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001098
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001099 def assert_equal(self, real_value, expected_value, name_or_class=None):
1100 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001101 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001102 return
1103 try:
1104 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1105 msg = msg % (getdoc(name_or_class).strip(),
1106 real_value, str(name_or_class(real_value)),
1107 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001108 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001109 msg = "Invalid %s: %s does not match expected value %s" % (
1110 name_or_class, real_value, expected_value)
1111
1112 self.assertEqual(real_value, expected_value, msg)
1113
Klement Sekerab17dd962017-01-09 07:43:48 +01001114 def assert_in_range(self,
1115 real_value,
1116 expected_min,
1117 expected_max,
1118 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001119 if name is None:
1120 msg = None
1121 else:
1122 msg = "Invalid %s: %s out of range <%s,%s>" % (
1123 name, real_value, expected_min, expected_max)
1124 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1125
Klement Sekerad81ae412018-05-16 10:52:54 +02001126 def assert_packet_checksums_valid(self, packet,
1127 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001128 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001129 udp_layers = ['UDP', 'UDPerror']
1130 checksum_fields = ['cksum', 'chksum']
1131 checksums = []
1132 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001133 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001134 while True:
1135 layer = temp.getlayer(counter)
1136 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001137 layer = layer.copy()
1138 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001139 for cf in checksum_fields:
1140 if hasattr(layer, cf):
1141 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001142 0 == getattr(layer, cf) and \
1143 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001144 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001145 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001146 checksums.append((counter, cf))
1147 else:
1148 break
1149 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001150 if 0 == len(checksums):
1151 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001152 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001153 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001154 calc_sum = getattr(temp[layer], cf)
1155 self.assert_equal(
1156 getattr(received[layer], cf), calc_sum,
1157 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1158 self.logger.debug(
1159 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1160 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001161
1162 def assert_checksum_valid(self, received_packet, layer,
1163 field_name='chksum',
1164 ignore_zero_checksum=False):
1165 """ Check checksum of received packet on given layer """
1166 received_packet_checksum = getattr(received_packet[layer], field_name)
1167 if ignore_zero_checksum and 0 == received_packet_checksum:
1168 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001169 recalculated = received_packet.__class__(
1170 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001171 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001172 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001173 self.assert_equal(received_packet_checksum,
1174 getattr(recalculated[layer], field_name),
1175 "packet checksum on layer: %s" % layer)
1176
1177 def assert_ip_checksum_valid(self, received_packet,
1178 ignore_zero_checksum=False):
1179 self.assert_checksum_valid(received_packet, 'IP',
1180 ignore_zero_checksum=ignore_zero_checksum)
1181
1182 def assert_tcp_checksum_valid(self, received_packet,
1183 ignore_zero_checksum=False):
1184 self.assert_checksum_valid(received_packet, 'TCP',
1185 ignore_zero_checksum=ignore_zero_checksum)
1186
1187 def assert_udp_checksum_valid(self, received_packet,
1188 ignore_zero_checksum=True):
1189 self.assert_checksum_valid(received_packet, 'UDP',
1190 ignore_zero_checksum=ignore_zero_checksum)
1191
1192 def assert_embedded_icmp_checksum_valid(self, received_packet):
1193 if received_packet.haslayer(IPerror):
1194 self.assert_checksum_valid(received_packet, 'IPerror')
1195 if received_packet.haslayer(TCPerror):
1196 self.assert_checksum_valid(received_packet, 'TCPerror')
1197 if received_packet.haslayer(UDPerror):
1198 self.assert_checksum_valid(received_packet, 'UDPerror',
1199 ignore_zero_checksum=True)
1200 if received_packet.haslayer(ICMPerror):
1201 self.assert_checksum_valid(received_packet, 'ICMPerror')
1202
1203 def assert_icmp_checksum_valid(self, received_packet):
1204 self.assert_checksum_valid(received_packet, 'ICMP')
1205 self.assert_embedded_icmp_checksum_valid(received_packet)
1206
1207 def assert_icmpv6_checksum_valid(self, pkt):
1208 if pkt.haslayer(ICMPv6DestUnreach):
1209 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1210 self.assert_embedded_icmp_checksum_valid(pkt)
1211 if pkt.haslayer(ICMPv6EchoRequest):
1212 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1213 if pkt.haslayer(ICMPv6EchoReply):
1214 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1215
Klement Sekera3a343d42019-05-16 14:35:46 +02001216 def get_packet_counter(self, counter):
1217 if counter.startswith("/"):
1218 counter_value = self.statistics.get_counter(counter)
1219 else:
1220 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001221 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001222 for i in range(1, len(counters) - 1):
1223 results = counters[i].split()
1224 if results[1] == counter:
1225 counter_value = int(results[0])
1226 break
1227 return counter_value
1228
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001229 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001230 counter_value = self.get_packet_counter(counter)
1231 self.assert_equal(counter_value, expected_value,
1232 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001233
Ole Troan233e4682019-05-16 15:01:34 +02001234 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001235 counter_value = self.statistics[counter].sum()
Ole Troan233e4682019-05-16 15:01:34 +02001236 self.assert_equal(counter_value, expected_value,
1237 "error counter `%s'" % counter)
1238
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001239 @classmethod
1240 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001241
1242 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1243 # * by Guido, only the main thread can be interrupted.
1244 # */
1245 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1246 if timeout == 0:
1247 # yield quantum
1248 if hasattr(os, 'sched_yield'):
1249 os.sched_yield()
1250 else:
1251 time.sleep(0)
1252 return
1253
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001254 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001255 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001256 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001257 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001258 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001259 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001260 "slept for %es instead of ~%es!",
1261 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001262
1263 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001264 "Finished sleep (%s) - slept %es (wanted %es)",
1265 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001266
Benoît Ganne8c45e512021-02-19 16:39:13 +01001267 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001268 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001269 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001270 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001271
1272 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1273 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001274 if not timeout:
1275 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001276 for i in self.pg_interfaces:
1277 i.get_capture(0, timeout=timeout)
1278 i.assert_nothing_captured(remark=remark)
1279 timeout = 0.1
1280
Benoît Ganne8c45e512021-02-19 16:39:13 +01001281 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1282 trace=True):
Neale Rannsd7603d92019-03-28 08:56:10 +00001283 if not n_rx:
1284 n_rx = len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001285 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001286 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001287 return rx
1288
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001289 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1290 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001291 rx = output.get_capture(len(pkts))
1292 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001293 if not timeout:
1294 timeout = 1
1295 for i in self.pg_interfaces:
1296 if i not in outputs:
1297 i.get_capture(0, timeout=timeout)
1298 i.assert_nothing_captured()
1299 timeout = 0.1
1300
Neale Ranns52fae862018-01-08 04:41:42 -08001301 return rx
1302
Damjan Marionf56b77a2016-10-03 19:44:57 +02001303
juraj.linkes184870a2018-07-16 14:22:01 +02001304def get_testcase_doc_name(test):
1305 return getdoc(test.__class__).splitlines()[0]
1306
1307
Ole Trøan5ba91592018-11-22 10:01:09 +00001308def get_test_description(descriptions, test):
1309 short_description = test.shortDescription()
1310 if descriptions and short_description:
1311 return short_description
1312 else:
1313 return str(test)
1314
1315
juraj.linkes40dd73b2018-09-21 13:55:16 +02001316class TestCaseInfo(object):
1317 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1318 self.logger = logger
1319 self.tempdir = tempdir
1320 self.vpp_pid = vpp_pid
1321 self.vpp_bin_path = vpp_bin_path
1322 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001323
1324
Damjan Marionf56b77a2016-10-03 19:44:57 +02001325class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001326 """
1327 @property result_string
1328 String variable to store the test case result string.
1329 @property errors
1330 List variable containing 2-tuples of TestCase instances and strings
1331 holding formatted tracebacks. Each tuple represents a test which
1332 raised an unexpected exception.
1333 @property failures
1334 List variable containing 2-tuples of TestCase instances and strings
1335 holding formatted tracebacks. Each tuple represents a test where
1336 a failure was explicitly signalled using the TestCase.assert*()
1337 methods.
1338 """
1339
juraj.linkes40dd73b2018-09-21 13:55:16 +02001340 failed_test_cases_info = set()
1341 core_crash_test_cases_info = set()
1342 current_test_case_info = None
1343
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001344 def __init__(self, stream=None, descriptions=None, verbosity=None,
1345 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001346 """
Klement Sekerada505f62017-01-04 12:58:53 +01001347 :param stream File descriptor to store where to report test results.
1348 Set to the standard error stream by default.
1349 :param descriptions Boolean variable to store information if to use
1350 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001351 :param verbosity Integer variable to store required verbosity level.
1352 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001353 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001354 self.stream = stream
1355 self.descriptions = descriptions
1356 self.verbosity = verbosity
1357 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001358 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001359 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001360
Damjan Marionf56b77a2016-10-03 19:44:57 +02001361 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001362 """
1363 Record a test succeeded result
1364
1365 :param test:
1366
1367 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001368 if self.current_test_case_info:
1369 self.current_test_case_info.logger.debug(
1370 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1371 test._testMethodName,
1372 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001373 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001374 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001375
juraj.linkescae64f82018-09-19 15:01:47 +02001376 self.send_result_through_pipe(test, PASS)
1377
Klement Sekeraf62ae122016-10-11 11:47:09 +02001378 def addSkip(self, test, reason):
1379 """
1380 Record a test skipped.
1381
1382 :param test:
1383 :param reason:
1384
1385 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001386 if self.current_test_case_info:
1387 self.current_test_case_info.logger.debug(
1388 "--- addSkip() %s.%s(%s) called, reason is %s" %
1389 (test.__class__.__name__, test._testMethodName,
1390 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001391 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001392 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001393
Klement Sekera558ceab2021-04-08 19:37:41 +02001394 if reason == "not enough cpus":
1395 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1396 else:
1397 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001398
juraj.linkes40dd73b2018-09-21 13:55:16 +02001399 def symlink_failed(self):
1400 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001401 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001402 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001403 link_path = os.path.join(
1404 failed_dir,
1405 '%s-FAILED' %
1406 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001407
1408 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001409 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001410 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001411 "os.symlink(%s, %s)" %
1412 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001413 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001414 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001415 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001416 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001417 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001418
Klement Sekeraf413bef2017-08-15 07:09:02 +02001419 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001420 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001421
juraj.linkescae64f82018-09-19 15:01:47 +02001422 def send_result_through_pipe(self, test, result):
1423 if hasattr(self, 'test_framework_result_pipe'):
1424 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001425 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001426 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001427
juraj.linkes40dd73b2018-09-21 13:55:16 +02001428 def log_error(self, test, err, fn_name):
1429 if self.current_test_case_info:
1430 if isinstance(test, unittest.suite._ErrorHolder):
1431 test_name = test.description
1432 else:
1433 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1434 test._testMethodName,
1435 test._testMethodDoc)
1436 self.current_test_case_info.logger.debug(
1437 "--- %s() %s called, err is %s" %
1438 (fn_name, test_name, err))
1439 self.current_test_case_info.logger.debug(
1440 "formatted exception is:\n%s" %
1441 "".join(format_exception(*err)))
1442
1443 def add_error(self, test, err, unittest_fn, error_type):
1444 if error_type == FAIL:
1445 self.log_error(test, err, 'addFailure')
1446 error_type_str = colorize("FAIL", RED)
1447 elif error_type == ERROR:
1448 self.log_error(test, err, 'addError')
1449 error_type_str = colorize("ERROR", RED)
1450 else:
1451 raise Exception('Error type %s cannot be used to record an '
1452 'error or a failure' % error_type)
1453
1454 unittest_fn(self, test, err)
1455 if self.current_test_case_info:
1456 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1457 (error_type_str,
1458 self.current_test_case_info.tempdir)
1459 self.symlink_failed()
1460 self.failed_test_cases_info.add(self.current_test_case_info)
1461 if is_core_present(self.current_test_case_info.tempdir):
1462 if not self.current_test_case_info.core_crash_test:
1463 if isinstance(test, unittest.suite._ErrorHolder):
1464 test_name = str(test)
1465 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001466 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001467 get_testcase_doc_name(test), test.id())
1468 self.current_test_case_info.core_crash_test = test_name
1469 self.core_crash_test_cases_info.add(
1470 self.current_test_case_info)
1471 else:
1472 self.result_string = '%s [no temp dir]' % error_type_str
1473
1474 self.send_result_through_pipe(test, error_type)
1475
Damjan Marionf56b77a2016-10-03 19:44:57 +02001476 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001477 """
1478 Record a test failed result
1479
1480 :param test:
1481 :param err: error message
1482
1483 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001484 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001485
Damjan Marionf56b77a2016-10-03 19:44:57 +02001486 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001487 """
1488 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001489
Klement Sekeraf62ae122016-10-11 11:47:09 +02001490 :param test:
1491 :param err: error message
1492
1493 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001494 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001495
Damjan Marionf56b77a2016-10-03 19:44:57 +02001496 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001497 """
1498 Get test description
1499
1500 :param test:
1501 :returns: test description
1502
1503 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001504 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001505
Damjan Marionf56b77a2016-10-03 19:44:57 +02001506 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001507 """
1508 Start a test
1509
1510 :param test:
1511
1512 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001513
1514 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001515 if test.__class__ in self.printed:
1516 return
1517
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001518 test_doc = getdoc(test)
1519 if not test_doc:
1520 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001521
Klement Sekeraea6236b2021-03-25 14:03:44 +01001522 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001523 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001524 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001525 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001526
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001527 # This block may overwrite the colorized title above,
1528 # but we want this to stand out and be fixed
1529 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekera558ceab2021-04-08 19:37:41 +02001530 test_title = colorize(
1531 f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001532
Klement Sekeraea6236b2021-03-25 14:03:44 +01001533 if hasattr(test, 'vpp_worker_count'):
1534 if test.vpp_worker_count == 0:
1535 test_title += " [main thread only]"
1536 elif test.vpp_worker_count == 1:
1537 test_title += " [1 worker thread]"
1538 else:
1539 test_title += f" [{test.vpp_worker_count} worker threads]"
1540
Klement Sekera558ceab2021-04-08 19:37:41 +02001541 if test.__class__.skipped_due_to_cpu_lack:
1542 test_title = colorize(
1543 f"{test_title} [skipped - not enough cpus, "
1544 f"required={test.__class__.get_cpus_required()}, "
1545 f"available={max_vpp_cpus}]", YELLOW)
1546
1547 print(double_line_delim)
1548 print(test_title)
1549 print(double_line_delim)
1550 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001551
1552 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001553 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001554 unittest.TestResult.startTest(self, test)
1555 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001556 self.stream.writeln(
1557 "Starting " + self.getDescription(test) + " ...")
1558 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001559
Damjan Marionf56b77a2016-10-03 19:44:57 +02001560 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001561 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001562 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001563
1564 :param test:
1565
1566 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001567 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001568
Damjan Marionf56b77a2016-10-03 19:44:57 +02001569 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001570 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001571 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001572 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001573 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001574 else:
Ole Troan0c629322019-11-28 14:48:44 +01001575 self.stream.writeln("%-68s %4.2f %s" %
1576 (self.getDescription(test),
1577 time.time() - self.start_test,
1578 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001579
1580 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001581
Damjan Marionf56b77a2016-10-03 19:44:57 +02001582 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001583 """
1584 Print errors from running the test case
1585 """
juraj.linkesabec0122018-11-16 17:28:56 +01001586 if len(self.errors) > 0 or len(self.failures) > 0:
1587 self.stream.writeln()
1588 self.printErrorList('ERROR', self.errors)
1589 self.printErrorList('FAIL', self.failures)
1590
1591 # ^^ that is the last output from unittest before summary
1592 if not self.runner.print_summary:
1593 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1594 self.stream = devnull
1595 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001596
Damjan Marionf56b77a2016-10-03 19:44:57 +02001597 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001598 """
1599 Print error list to the output stream together with error type
1600 and test case description.
1601
1602 :param flavour: error type
1603 :param errors: iterable errors
1604
1605 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001606 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001607 self.stream.writeln(double_line_delim)
1608 self.stream.writeln("%s: %s" %
1609 (flavour, self.getDescription(test)))
1610 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001611 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001612
1613
Damjan Marionf56b77a2016-10-03 19:44:57 +02001614class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001615 """
Klement Sekera104543f2017-02-03 07:29:43 +01001616 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001617 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001618
Klement Sekeraf62ae122016-10-11 11:47:09 +02001619 @property
1620 def resultclass(self):
1621 """Class maintaining the results of the tests"""
1622 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001623
juraj.linkes184870a2018-07-16 14:22:01 +02001624 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001625 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001626 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001627 # ignore stream setting here, use hard-coded stdout to be in sync
1628 # with prints from VppTestCase methods ...
1629 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1630 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001631 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001632 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001633
juraj.linkesabec0122018-11-16 17:28:56 +01001634 self.orig_stream = self.stream
1635 self.resultclass.test_framework_result_pipe = result_pipe
1636
1637 self.print_summary = print_summary
1638
1639 def _makeResult(self):
1640 return self.resultclass(self.stream,
1641 self.descriptions,
1642 self.verbosity,
1643 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001644
Damjan Marionf56b77a2016-10-03 19:44:57 +02001645 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001646 """
1647 Run the tests
1648
1649 :param test:
1650
1651 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001652 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001653
1654 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001655 if not self.print_summary:
1656 self.stream = self.orig_stream
1657 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001658 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001659
1660
1661class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001662 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1663 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001664 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001665 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001666 if hasattr(self, 'testcase') and self.testcase.debug_all:
1667 if self.testcase.debug_gdbserver:
1668 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1669 .format(port=self.testcase.gdbserver_port)] + args
1670 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1671 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001672 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001673 self.app_name = os.path.basename(self.app_bin)
1674 if hasattr(self, 'role'):
1675 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001676 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001677 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001678 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001679 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001680
Dave Wallace24564332019-10-21 02:53:14 +00001681 def wait_for_enter(self):
1682 if not hasattr(self, 'testcase'):
1683 return
1684 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1685 print()
1686 print(double_line_delim)
1687 print("Spawned GDB Server for '{app}' with PID: {pid}"
1688 .format(app=self.app_name, pid=self.process.pid))
1689 elif self.testcase.debug_all and self.testcase.debug_gdb:
1690 print()
1691 print(double_line_delim)
1692 print("Spawned '{app}' with PID: {pid}"
1693 .format(app=self.app_name, pid=self.process.pid))
1694 else:
1695 return
1696 print(single_line_delim)
1697 print("You can debug '{app}' using:".format(app=self.app_name))
1698 if self.testcase.debug_gdbserver:
1699 print("sudo gdb " + self.app_bin +
1700 " -ex 'target remote localhost:{port}'"
1701 .format(port=self.testcase.gdbserver_port))
1702 print("Now is the time to attach gdb by running the above "
1703 "command, set up breakpoints etc., then resume from "
1704 "within gdb by issuing the 'continue' command")
1705 self.testcase.gdbserver_port += 1
1706 elif self.testcase.debug_gdb:
1707 print("sudo gdb " + self.app_bin +
1708 " -ex 'attach {pid}'".format(pid=self.process.pid))
1709 print("Now is the time to attach gdb by running the above "
1710 "command and set up breakpoints etc., then resume from"
1711 " within gdb by issuing the 'continue' command")
1712 print(single_line_delim)
1713 input("Press ENTER to continue running the testcase...")
1714
Neale Ranns812ed392017-10-16 04:20:13 -07001715 def run(self):
1716 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001717 if not os.path.exists(executable) or not os.access(
1718 executable, os.F_OK | os.X_OK):
1719 # Exit code that means some system file did not exist,
1720 # could not be opened, or had some other kind of error.
1721 self.result = os.EX_OSFILE
1722 raise EnvironmentError(
1723 "executable '%s' is not found or executable." % executable)
Dave Wallace4ee17d82021-05-20 14:01:51 -04001724 self.logger.debug("Running executable '{app}': '{cmd}'"
1725 .format(app=self.app_name,
1726 cmd=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001727 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001728 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001729 env["CK_LOG_FILE_NAME"] = "-"
1730 self.process = subprocess.Popen(
Dave Wallaceae8d3322021-05-18 17:12:16 -04001731 ['stdbuf', '-o0', '-e0'] + self.args, shell=False, env=env,
1732 preexec_fn=os.setpgrp, stdout=subprocess.PIPE,
1733 stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001734 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001735 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001736 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001737 self.logger.info("Return code is `%s'" % self.process.returncode)
1738 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001739 self.logger.info("Executable `{app}' wrote to stdout:"
1740 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001741 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001742 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001743 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001744 self.logger.info("Executable `{app}' wrote to stderr:"
1745 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001746 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001747 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001748 self.logger.info(single_line_delim)
1749 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001750
Klement Sekera6aa58b72019-05-16 14:34:55 +02001751
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001752if __name__ == '__main__':
1753 pass