blob: 1cbd814aa8d82e26535a94c946b39c39c7eb8667 [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
Ole Troana45dc072018-12-21 16:04:22 +0100333 extra_vpp_punt_config = []
334 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500335 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400336 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100337
Klement Sekeraf62ae122016-10-11 11:47:09 +0200338 @property
339 def packet_infos(self):
340 """List of packet infos"""
341 return self._packet_infos
342
Klement Sekeradab231a2016-12-21 08:50:14 +0100343 @classmethod
344 def get_packet_count_for_if_idx(cls, dst_if_index):
345 """Get the number of packet info for specified destination if index"""
346 if dst_if_index in cls._packet_count_for_dst_if_idx:
347 return cls._packet_count_for_dst_if_idx[dst_if_index]
348 else:
349 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200350
351 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000352 def has_tag(cls, tag):
353 """ if the test case has a given tag - return true """
354 try:
355 return tag in cls.test_tags
356 except AttributeError:
357 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000358 return False
359
360 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000361 def is_tagged_run_solo(cls):
362 """ if the test case class is timing-sensitive - return true """
363 return cls.has_tag(TestCaseTag.RUN_SOLO)
364
365 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200366 def instance(cls):
367 """Return the instance of this testcase"""
368 return cls.test_instance
369
Damjan Marionf56b77a2016-10-03 19:44:57 +0200370 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200371 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000372 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200373 cls.debug_core = False
374 cls.debug_gdb = False
375 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000376 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100377 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200378 if d is None:
379 return
380 dl = d.lower()
381 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200382 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000383 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200384 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000385 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200386 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100387 elif dl == "attach":
388 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200389 else:
390 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000391 if dl == "gdb-all" or dl == "gdbserver-all":
392 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200393
Klement Sekera558ceab2021-04-08 19:37:41 +0200394 @classmethod
395 def get_vpp_worker_count(cls):
396 if not hasattr(cls, "vpp_worker_count"):
397 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
398 cls.vpp_worker_count = 0
399 else:
400 cls.vpp_worker_count = environ_vpp_worker_count
401 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200402
Klement Sekera558ceab2021-04-08 19:37:41 +0200403 @classmethod
404 def get_cpus_required(cls):
405 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200406
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800407 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200408 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200409 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400410 cls.step = BoolEnvironmentVariable('STEP')
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400411 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100412 c = os.getenv("CACHE_OUTPUT", "1")
413 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekerab8c72a42018-11-08 11:21:39 +0100414 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
415 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400416 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100417 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
418 plugin_path = None
419 if cls.plugin_path is not None:
420 if cls.extern_plugin_path is not None:
421 plugin_path = "%s:%s" % (
422 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100423 else:
424 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100425 elif cls.extern_plugin_path is not None:
426 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100427 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100428 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100429 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100430 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100431 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100432 if size is not None:
433 coredump_size = "coredump-size %s" % size
434 if coredump_size is None:
435 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200436
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000437 default_variant = os.getenv("VARIANT")
438 if default_variant is not None:
439 default_variant = "defaults { %s 100 }" % default_variant
440 else:
441 default_variant = ""
442
Dave Barach77841402020-04-29 17:04:10 -0400443 api_fuzzing = os.getenv("API_FUZZ")
444 if api_fuzzing is None:
445 api_fuzzing = 'off'
446
Klement Sekera8d815022021-03-15 16:58:10 +0100447 cls.vpp_cmdline = [
448 cls.vpp_bin,
449 "unix", "{", "nodaemon", debug_cli, "full-coredump",
450 coredump_size, "runtime-dir", cls.tempdir, "}",
451 "api-trace", "{", "on", "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100452 "api-segment", "{", "prefix", cls.get_api_segment_prefix(), "}",
Klement Sekera558ceab2021-04-08 19:37:41 +0200453 "cpu", "{", "main-core", str(cls.cpus[0]), ]
454 if cls.get_vpp_worker_count():
455 cls.vpp_cmdline.extend([
456 "corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])])
Klement Sekera8d815022021-03-15 16:58:10 +0100457 cls.vpp_cmdline.extend([
458 "}",
459 "physmem", "{", "max-size", "32m", "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100460 "statseg", "{", "socket-name", cls.get_stats_sock_path(), "}",
461 "socksvr", "{", "socket-name", cls.get_api_sock_path(), "}",
Klement Sekera8d815022021-03-15 16:58:10 +0100462 "node { ", default_variant, "}",
463 "api-fuzz {", api_fuzzing, "}",
464 "plugins", "{", "plugin", "dpdk_plugin.so", "{", "disable", "}",
465 "plugin", "rdma_plugin.so", "{", "disable", "}",
466 "plugin", "lisp_unittest_plugin.so", "{", "enable", "}",
467 "plugin", "unittest_plugin.so", "{", "enable", "}"
468 ] + cls.extra_vpp_plugin_config + ["}", ])
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000469
Ole Troana45dc072018-12-21 16:04:22 +0100470 if cls.extra_vpp_punt_config is not None:
471 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100472 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100473 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400474 if cls.test_plugin_path is not None:
475 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
476
Klement Sekerae2636852021-03-16 12:52:12 +0100477 if not cls.debug_attach:
478 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
479 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200480
481 @classmethod
482 def wait_for_enter(cls):
483 if cls.debug_gdbserver:
484 print(double_line_delim)
485 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
486 elif cls.debug_gdb:
487 print(double_line_delim)
488 print("Spawned VPP with PID: %d" % cls.vpp.pid)
489 else:
490 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
491 return
492 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000493 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200494 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400495 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000496 " -ex 'target remote localhost:{port}'"
497 .format(port=cls.gdbserver_port))
498 print("Now is the time to attach gdb by running the above "
499 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200500 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000501 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200502 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400503 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000504 print("Now is the time to attach gdb by running the above "
505 "command and set up breakpoints etc., then resume VPP from"
506 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200507 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800508 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200509
510 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100511 def attach_vpp(cls):
512 cls.vpp = DummyVpp()
513
514 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200515 def run_vpp(cls):
Klement Sekera558ceab2021-04-08 19:37:41 +0200516 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200517 cmdline = cls.vpp_cmdline
518
519 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100520 gdbserver = '/usr/bin/gdbserver'
Klement Sekera558ceab2021-04-08 19:37:41 +0200521 if not os.path.isfile(gdbserver) or\
Klement Sekera931be3a2016-11-03 05:36:01 +0100522 not os.access(gdbserver, os.X_OK):
523 raise Exception("gdbserver binary '%s' does not exist or is "
524 "not executable" % gdbserver)
525
Dave Wallace24564332019-10-21 02:53:14 +0000526 cmdline = [gdbserver, 'localhost:{port}'
527 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200528 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
529
Klement Sekera931be3a2016-11-03 05:36:01 +0100530 try:
531 cls.vpp = subprocess.Popen(cmdline,
532 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100533 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800534 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800535 cls.logger.critical("Subprocess returned with non-0 return code: ("
536 "%s)", e.returncode)
537 raise
538 except OSError as e:
539 cls.logger.critical("Subprocess returned with OS error: "
540 "(%s) %s", e.errno, e.strerror)
541 raise
542 except Exception as e:
543 cls.logger.exception("Subprocess returned unexpected from "
544 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100545 raise
546
Klement Sekera277b89c2016-10-28 13:20:27 +0200547 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100548
Damjan Marionf56b77a2016-10-03 19:44:57 +0200549 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000550 def wait_for_coredump(cls):
551 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400552 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000553 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400554 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000555 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400556 ok = False
557 while time.time() < deadline:
558 cls.sleep(1)
559 size = curr_size
560 curr_size = os.path.getsize(corefile)
561 if size == curr_size:
562 ok = True
563 break
564 if not ok:
565 cls.logger.error("Timed out waiting for coredump to complete:"
566 " %s", corefile)
567 else:
568 cls.logger.error("Coredump complete: %s, size %d",
569 corefile, curr_size)
570
571 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100572 def get_stats_sock_path(cls):
573 return "%s/stats.sock" % cls.tempdir
574
575 @classmethod
576 def get_api_sock_path(cls):
577 return "%s/api.sock" % cls.tempdir
578
579 @classmethod
580 def get_api_segment_prefix(cls):
581 return os.path.basename(cls.tempdir) # Only used for VAPI
582
583 @classmethod
584 def get_tempdir(cls):
585 if cls.debug_attach:
586 return os.getenv("VPP_IN_GDB_TMP_DIR",
587 "/tmp/unittest-attach-gdb")
588 else:
589 return tempfile.mkdtemp(prefix='vpp-unittest-%s-' % cls.__name__)
590
591 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200592 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200593 """
594 Perform class setup before running the testcase
595 Remove shared memory files, start vpp and connect the vpp-api
596 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800597 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100598 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000599 seed = os.environ["RND_SEED"]
600 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100601 if hasattr(cls, 'parallel_handler'):
602 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100603 cls.logger.propagate = False
Klement Sekerae2636852021-03-16 12:52:12 +0100604 d = os.getenv("DEBUG", None)
605 cls.set_debug_flags(d)
606 cls.tempdir = cls.get_tempdir()
Klement Sekera027dbd52017-04-11 06:01:53 +0200607 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
608 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100609 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
610 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200611 cls.file_handler.setLevel(DEBUG)
612 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100613 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200614 os.chdir(cls.tempdir)
Ole Troan4376ab22021-03-03 10:40:05 +0100615 cls.logger.info("Temporary dir is %s, api socket is %s",
Klement Sekerae2636852021-03-16 12:52:12 +0100616 cls.tempdir, cls.get_api_sock_path())
617 cls.logger.debug("Random seed is %s", seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200618 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100619 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200620 cls._pcaps = []
621 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200622 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100623 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100624 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200625 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200626 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200627 # need to catch exceptions here because if we raise, then the cleanup
628 # doesn't get called and we might end with a zombie vpp
629 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100630 if cls.debug_attach:
631 cls.attach_vpp()
632 else:
633 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200634 cls.reporter.send_keep_alive(cls, 'setUpClass')
635 VppTestResult.current_test_case_info = TestCaseInfo(
636 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100637 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100638 cls.vpp_stderr_deque = deque()
Klement Sekerae2636852021-03-16 12:52:12 +0100639 if not cls.debug_attach:
640 cls.pump_thread_stop_flag = Event()
641 cls.pump_thread_wakeup_pipe = os.pipe()
642 cls.pump_thread = Thread(target=pump_output, args=(cls,))
643 cls.pump_thread.daemon = True
644 cls.pump_thread.start()
645 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400646 cls.vapi_response_timeout = 0
Ole Troan4376ab22021-03-03 10:40:05 +0100647 cls.vapi = VppPapiProvider(cls.__name__, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400648 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100649 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400650 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100651 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400652 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100653 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100654 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200655 try:
656 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100657 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200658 cls.vpp_startup_failed = True
659 cls.logger.critical(
660 "VPP died shortly after startup, check the"
661 " output to standard error for possible cause")
662 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100663 try:
664 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100665 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500666 cls.logger.debug("Exception connecting to vapi: %s" % e)
667 cls.vapi.disconnect()
668
Klement Sekera085f5c02016-11-24 01:59:16 +0100669 if cls.debug_gdbserver:
670 print(colorize("You're running VPP inside gdbserver but "
671 "VPP-API connection failed, did you forget "
672 "to 'continue' VPP from within gdb?", RED))
Ole Troan4376ab22021-03-03 10:40:05 +0100673 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100674 if cls.debug_attach:
675 last_line = cls.vapi.cli("show thread").split("\n")[-2]
676 cls.vpp_worker_count = int(last_line.split(" ")[0])
677 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400678 except vpp_papi.VPPRuntimeError as e:
679 cls.logger.debug("%s" % e)
680 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100681 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000682 except Exception as e:
683 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400684 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100685 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200686
Damjan Marionf56b77a2016-10-03 19:44:57 +0200687 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500688 def _debug_quit(cls):
689 if (cls.debug_gdbserver or cls.debug_gdb):
690 try:
691 cls.vpp.poll()
692
693 if cls.vpp.returncode is None:
694 print()
695 print(double_line_delim)
696 print("VPP or GDB server is still running")
697 print(single_line_delim)
698 input("When done debugging, press ENTER to kill the "
699 "process and finish running the testcase...")
700 except AttributeError:
701 pass
702
703 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200704 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200705 """
706 Disconnect vpp-api, kill vpp and cleanup shared memory files
707 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500708 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200709
juraj.linkes184870a2018-07-16 14:22:01 +0200710 # first signal that we want to stop the pump thread, then wake it up
711 if hasattr(cls, 'pump_thread_stop_flag'):
712 cls.pump_thread_stop_flag.set()
713 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100714 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100715 if hasattr(cls, 'pump_thread'):
716 cls.logger.debug("Waiting for pump thread to stop")
717 cls.pump_thread.join()
718 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500719 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100720 cls.vpp_stderr_reader_thread.join()
721
Klement Sekeraf62ae122016-10-11 11:47:09 +0200722 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100723 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100724 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700725 cls.logger.debug("Disconnecting class vapi client on %s",
726 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100727 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700728 cls.logger.debug("Deleting class vapi attribute on %s",
729 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100730 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200731 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100732 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000733 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100734 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400735 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100736 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100737 try:
738 outs, errs = cls.vpp.communicate(timeout=5)
739 except subprocess.TimeoutExpired:
740 cls.vpp.kill()
741 outs, errs = cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700742 cls.logger.debug("Deleting class vpp attribute on %s",
743 cls.__name__)
Klement Sekerae2636852021-03-16 12:52:12 +0100744 if not cls.debug_attach:
745 cls.vpp.stdout.close()
746 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200747 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200748
Klement Sekera3747c752017-04-10 06:30:17 +0200749 if cls.vpp_startup_failed:
750 stdout_log = cls.logger.info
751 stderr_log = cls.logger.critical
752 else:
753 stdout_log = cls.logger.info
754 stderr_log = cls.logger.info
755
Klement Sekerae4504c62016-12-08 10:16:41 +0100756 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200757 stdout_log(single_line_delim)
758 stdout_log('VPP output to stdout while running %s:', cls.__name__)
759 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100760 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200761 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
762 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200763 stdout_log('\n%s', vpp_output)
764 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200765
Klement Sekerae4504c62016-12-08 10:16:41 +0100766 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200767 stderr_log(single_line_delim)
768 stderr_log('VPP output to stderr while running %s:', cls.__name__)
769 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100770 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200771 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
772 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200773 stderr_log('\n%s', vpp_output)
774 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200775
Damjan Marionf56b77a2016-10-03 19:44:57 +0200776 @classmethod
777 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200778 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700779 cls.logger.debug("--- tearDownClass() for %s called ---" %
780 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200781 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200782 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200783 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100784 cls.reset_packet_infos()
785 if debug_framework:
786 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200787
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700788 def show_commands_at_teardown(self):
789 """ Allow subclass specific teardown logging additions."""
790 self.logger.info("--- No test specific show commands provided. ---")
791
Damjan Marionf56b77a2016-10-03 19:44:57 +0200792 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200793 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100794 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
795 (self.__class__.__name__, self._testMethodName,
796 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700797
798 try:
799 if not self.vpp_dead:
800 self.logger.debug(self.vapi.cli("show trace max 1000"))
801 self.logger.info(self.vapi.ppcli("show interface"))
802 self.logger.info(self.vapi.ppcli("show hardware"))
803 self.logger.info(self.statistics.set_errors_str())
804 self.logger.info(self.vapi.ppcli("show run"))
805 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400806 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700807 self.logger.info("Logging testcase specific show commands.")
808 self.show_commands_at_teardown()
809 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500810 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000811 m = self._testMethodName
812 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500813 tmp_api_trace = "/tmp/%s" % api_trace
814 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
815 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
816 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
817 vpp_api_trace_log))
818 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500819 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500820 vpp_api_trace_log))
Ole Troan4376ab22021-03-03 10:40:05 +0100821 except VppTransportSocketIOError:
822 self.logger.debug("VppTransportSocketIOError: Vpp dead. "
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700823 "Cannot log show commands.")
824 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100825 else:
826 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200827
Damjan Marionf56b77a2016-10-03 19:44:57 +0200828 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200829 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800830 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200831 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100832 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400833 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
834 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100835 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100836 self.vpp_stdout_deque.append(
837 "--- test setUp() for %s.%s(%s) starts here ---\n" %
838 (self.__class__.__name__, self._testMethodName,
839 self._testMethodDoc))
840 self.vpp_stderr_deque.append(
841 "--- test setUp() for %s.%s(%s) starts here ---\n" %
842 (self.__class__.__name__, self._testMethodName,
843 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200844 self.vapi.cli("clear trace")
845 # store the test instance inside the test class - so that objects
846 # holding the class can access instance methods (like assertEqual)
847 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200848
Damjan Marionf56b77a2016-10-03 19:44:57 +0200849 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200850 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200851 """
852 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200853
Klement Sekera75e7d132017-09-20 08:26:30 +0200854 :param interfaces: iterable interface indexes (if None,
855 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200856
Klement Sekeraf62ae122016-10-11 11:47:09 +0200857 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200858 if interfaces is None:
859 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200860 for i in interfaces:
861 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200862
Damjan Marionf56b77a2016-10-03 19:44:57 +0200863 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200864 def register_pcap(cls, intf, worker):
865 """ Register a pcap in the testclass """
Klement Sekera9225dee2016-12-12 08:36:58 +0100866 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200867 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100868
869 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000870 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400871 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
872 # returns float("2.190522")
873 timestr = cls.vapi.cli('show clock')
874 head, sep, tail = timestr.partition(',')
875 head, sep, tail = head.partition('Time now')
876 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000877
878 @classmethod
879 def sleep_on_vpp_time(cls, sec):
880 """ Sleep according to time in VPP world """
881 # On a busy system with many processes
882 # we might end up with VPP time being slower than real world
883 # So take that into account when waiting for VPP to do something
884 start_time = cls.get_vpp_time()
885 while cls.get_vpp_time() - start_time < sec:
886 cls.sleep(0.1)
887
888 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100889 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000890 """ Enable the PG, wait till it is done, then clean up """
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200891 for (intf, worker) in cls._old_pcaps:
892 intf.rename_old_pcap_file(intf.get_in_path(worker),
893 intf.in_history_counter)
894 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100895 if trace:
896 cls.vapi.cli("clear trace")
897 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200898 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000899 # PG, when starts, runs to completion -
900 # so let's avoid a race condition,
901 # and wait a little till it's done.
902 # Then clean it up - and then be gone.
903 deadline = time.time() + 300
904 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
905 cls.sleep(0.01) # yield
906 if time.time() > deadline:
907 cls.logger.error("Timeout waiting for pg to stop")
908 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200909 for intf, worker in cls._pcaps:
Klement Sekera7ba9fae2021-03-31 13:36:38 +0200910 cls.vapi.cli('packet-generator delete %s' %
911 intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200912 cls._old_pcaps = cls._pcaps
913 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200914
Damjan Marionf56b77a2016-10-03 19:44:57 +0200915 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200916 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200917 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100918 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200919
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100920 :param interfaces: iterable indexes of the interfaces.
921 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200922
Klement Sekeraf62ae122016-10-11 11:47:09 +0200923 """
924 result = []
925 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200926 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200927 setattr(cls, intf.name, intf)
928 result.append(intf)
929 cls.pg_interfaces = result
930 return result
931
Matej Klotton0178d522016-11-04 11:11:44 +0100932 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200933 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100934 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100935 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100936
Klement Sekerab9ef2732018-06-24 22:49:33 +0200937 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100938 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100939 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200940 result = [VppLoInterface(cls) for i in range(count)]
941 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100942 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100943 cls.lo_interfaces = result
944 return result
945
Neale Ranns192b13f2019-03-15 02:16:20 -0700946 @classmethod
947 def create_bvi_interfaces(cls, count):
948 """
949 Create BVI interfaces.
950
951 :param count: number of interfaces created.
952 :returns: List of created interfaces.
953 """
954 result = [VppBviInterface(cls) for i in range(count)]
955 for intf in result:
956 setattr(cls, intf.name, intf)
957 cls.bvi_interfaces = result
958 return result
959
Damjan Marionf56b77a2016-10-03 19:44:57 +0200960 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200961 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200962 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200963 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200964 NOTE: Currently works only when Raw layer is present.
965
966 :param packet: packet
967 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200968 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200969
970 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200971 packet_len = len(packet) + 4
972 extend = size - packet_len
973 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200974 num = (extend // len(padding)) + 1
975 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200976
Klement Sekeradab231a2016-12-21 08:50:14 +0100977 @classmethod
978 def reset_packet_infos(cls):
979 """ Reset the list of packet info objects and packet counts to zero """
980 cls._packet_infos = {}
981 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200982
Klement Sekeradab231a2016-12-21 08:50:14 +0100983 @classmethod
984 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200985 """
986 Create packet info object containing the source and destination indexes
987 and add it to the testcase's packet info list
988
Klement Sekeradab231a2016-12-21 08:50:14 +0100989 :param VppInterface src_if: source interface
990 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200991
992 :returns: _PacketInfo object
993
994 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200995 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100996 info.index = len(cls._packet_infos)
997 info.src = src_if.sw_if_index
998 info.dst = dst_if.sw_if_index
999 if isinstance(dst_if, VppSubInterface):
1000 dst_idx = dst_if.parent.sw_if_index
1001 else:
1002 dst_idx = dst_if.sw_if_index
1003 if dst_idx in cls._packet_count_for_dst_if_idx:
1004 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1005 else:
1006 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1007 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001008 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001009
Damjan Marionf56b77a2016-10-03 19:44:57 +02001010 @staticmethod
1011 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001012 """
1013 Convert _PacketInfo object to packet payload
1014
1015 :param info: _PacketInfo object
1016
1017 :returns: string containing serialized data from packet info
1018 """
Pavel Kotucek59dda062017-03-02 15:22:47 +01001019 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
1020 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001021
Damjan Marionf56b77a2016-10-03 19:44:57 +02001022 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001023 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001024 """
1025 Convert packet payload to _PacketInfo object
1026
1027 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001028 :type payload: <class 'scapy.packet.Raw'>
1029 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001030 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001031 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001032 :returns: _PacketInfo object containing de-serialized data from payload
1033
1034 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001035 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001036 info = _PacketInfo()
1037 info.index = int(numbers[0])
1038 info.src = int(numbers[1])
1039 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +01001040 info.ip = int(numbers[3])
1041 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +02001042 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001043
Damjan Marionf56b77a2016-10-03 19:44:57 +02001044 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001045 """
1046 Iterate over the packet info list stored in the testcase
1047 Start iteration with first element if info is None
1048 Continue based on index in info if info is specified
1049
1050 :param info: info or None
1051 :returns: next info in list or None if no more infos
1052 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001053 if info is None:
1054 next_index = 0
1055 else:
1056 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001057 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001058 return None
1059 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001060 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001061
Klement Sekeraf62ae122016-10-11 11:47:09 +02001062 def get_next_packet_info_for_interface(self, src_index, info):
1063 """
1064 Search the packet info list for the next packet info with same source
1065 interface index
1066
1067 :param src_index: source interface index to search for
1068 :param info: packet info - where to start the search
1069 :returns: packet info or None
1070
1071 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001072 while True:
1073 info = self.get_next_packet_info(info)
1074 if info is None:
1075 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001076 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001077 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001078
Klement Sekeraf62ae122016-10-11 11:47:09 +02001079 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1080 """
1081 Search the packet info list for the next packet info with same source
1082 and destination interface indexes
1083
1084 :param src_index: source interface index to search for
1085 :param dst_index: destination interface index to search for
1086 :param info: packet info - where to start the search
1087 :returns: packet info or None
1088
1089 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001090 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001091 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001092 if info is None:
1093 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001094 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001095 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001096
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001097 def assert_equal(self, real_value, expected_value, name_or_class=None):
1098 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001099 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001100 return
1101 try:
1102 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1103 msg = msg % (getdoc(name_or_class).strip(),
1104 real_value, str(name_or_class(real_value)),
1105 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001106 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001107 msg = "Invalid %s: %s does not match expected value %s" % (
1108 name_or_class, real_value, expected_value)
1109
1110 self.assertEqual(real_value, expected_value, msg)
1111
Klement Sekerab17dd962017-01-09 07:43:48 +01001112 def assert_in_range(self,
1113 real_value,
1114 expected_min,
1115 expected_max,
1116 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001117 if name is None:
1118 msg = None
1119 else:
1120 msg = "Invalid %s: %s out of range <%s,%s>" % (
1121 name, real_value, expected_min, expected_max)
1122 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1123
Klement Sekerad81ae412018-05-16 10:52:54 +02001124 def assert_packet_checksums_valid(self, packet,
1125 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001126 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001127 udp_layers = ['UDP', 'UDPerror']
1128 checksum_fields = ['cksum', 'chksum']
1129 checksums = []
1130 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001131 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001132 while True:
1133 layer = temp.getlayer(counter)
1134 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001135 layer = layer.copy()
1136 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001137 for cf in checksum_fields:
1138 if hasattr(layer, cf):
1139 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001140 0 == getattr(layer, cf) and \
1141 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001142 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001143 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001144 checksums.append((counter, cf))
1145 else:
1146 break
1147 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001148 if 0 == len(checksums):
1149 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001150 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001151 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001152 calc_sum = getattr(temp[layer], cf)
1153 self.assert_equal(
1154 getattr(received[layer], cf), calc_sum,
1155 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1156 self.logger.debug(
1157 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1158 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001159
1160 def assert_checksum_valid(self, received_packet, layer,
1161 field_name='chksum',
1162 ignore_zero_checksum=False):
1163 """ Check checksum of received packet on given layer """
1164 received_packet_checksum = getattr(received_packet[layer], field_name)
1165 if ignore_zero_checksum and 0 == received_packet_checksum:
1166 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001167 recalculated = received_packet.__class__(
1168 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001169 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001170 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001171 self.assert_equal(received_packet_checksum,
1172 getattr(recalculated[layer], field_name),
1173 "packet checksum on layer: %s" % layer)
1174
1175 def assert_ip_checksum_valid(self, received_packet,
1176 ignore_zero_checksum=False):
1177 self.assert_checksum_valid(received_packet, 'IP',
1178 ignore_zero_checksum=ignore_zero_checksum)
1179
1180 def assert_tcp_checksum_valid(self, received_packet,
1181 ignore_zero_checksum=False):
1182 self.assert_checksum_valid(received_packet, 'TCP',
1183 ignore_zero_checksum=ignore_zero_checksum)
1184
1185 def assert_udp_checksum_valid(self, received_packet,
1186 ignore_zero_checksum=True):
1187 self.assert_checksum_valid(received_packet, 'UDP',
1188 ignore_zero_checksum=ignore_zero_checksum)
1189
1190 def assert_embedded_icmp_checksum_valid(self, received_packet):
1191 if received_packet.haslayer(IPerror):
1192 self.assert_checksum_valid(received_packet, 'IPerror')
1193 if received_packet.haslayer(TCPerror):
1194 self.assert_checksum_valid(received_packet, 'TCPerror')
1195 if received_packet.haslayer(UDPerror):
1196 self.assert_checksum_valid(received_packet, 'UDPerror',
1197 ignore_zero_checksum=True)
1198 if received_packet.haslayer(ICMPerror):
1199 self.assert_checksum_valid(received_packet, 'ICMPerror')
1200
1201 def assert_icmp_checksum_valid(self, received_packet):
1202 self.assert_checksum_valid(received_packet, 'ICMP')
1203 self.assert_embedded_icmp_checksum_valid(received_packet)
1204
1205 def assert_icmpv6_checksum_valid(self, pkt):
1206 if pkt.haslayer(ICMPv6DestUnreach):
1207 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1208 self.assert_embedded_icmp_checksum_valid(pkt)
1209 if pkt.haslayer(ICMPv6EchoRequest):
1210 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1211 if pkt.haslayer(ICMPv6EchoReply):
1212 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1213
Klement Sekera3a343d42019-05-16 14:35:46 +02001214 def get_packet_counter(self, counter):
1215 if counter.startswith("/"):
1216 counter_value = self.statistics.get_counter(counter)
1217 else:
1218 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001219 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001220 for i in range(1, len(counters) - 1):
1221 results = counters[i].split()
1222 if results[1] == counter:
1223 counter_value = int(results[0])
1224 break
1225 return counter_value
1226
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001227 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001228 counter_value = self.get_packet_counter(counter)
1229 self.assert_equal(counter_value, expected_value,
1230 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001231
Ole Troan233e4682019-05-16 15:01:34 +02001232 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001233 counter_value = self.statistics[counter].sum()
Ole Troan233e4682019-05-16 15:01:34 +02001234 self.assert_equal(counter_value, expected_value,
1235 "error counter `%s'" % counter)
1236
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001237 @classmethod
1238 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001239
1240 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1241 # * by Guido, only the main thread can be interrupted.
1242 # */
1243 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1244 if timeout == 0:
1245 # yield quantum
1246 if hasattr(os, 'sched_yield'):
1247 os.sched_yield()
1248 else:
1249 time.sleep(0)
1250 return
1251
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001252 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001253 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001254 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001255 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001256 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001257 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001258 "slept for %es instead of ~%es!",
1259 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001260
1261 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001262 "Finished sleep (%s) - slept %es (wanted %es)",
1263 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001264
Benoît Ganne8c45e512021-02-19 16:39:13 +01001265 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001266 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001267 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001268 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001269
1270 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1271 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001272 if not timeout:
1273 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001274 for i in self.pg_interfaces:
1275 i.get_capture(0, timeout=timeout)
1276 i.assert_nothing_captured(remark=remark)
1277 timeout = 0.1
1278
Benoît Ganne8c45e512021-02-19 16:39:13 +01001279 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1280 trace=True):
Neale Rannsd7603d92019-03-28 08:56:10 +00001281 if not n_rx:
1282 n_rx = len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001283 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001284 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001285 return rx
1286
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001287 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1288 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001289 rx = output.get_capture(len(pkts))
1290 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001291 if not timeout:
1292 timeout = 1
1293 for i in self.pg_interfaces:
1294 if i not in outputs:
1295 i.get_capture(0, timeout=timeout)
1296 i.assert_nothing_captured()
1297 timeout = 0.1
1298
Neale Ranns52fae862018-01-08 04:41:42 -08001299 return rx
1300
Damjan Marionf56b77a2016-10-03 19:44:57 +02001301
juraj.linkes184870a2018-07-16 14:22:01 +02001302def get_testcase_doc_name(test):
1303 return getdoc(test.__class__).splitlines()[0]
1304
1305
Ole Trøan5ba91592018-11-22 10:01:09 +00001306def get_test_description(descriptions, test):
1307 short_description = test.shortDescription()
1308 if descriptions and short_description:
1309 return short_description
1310 else:
1311 return str(test)
1312
1313
juraj.linkes40dd73b2018-09-21 13:55:16 +02001314class TestCaseInfo(object):
1315 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1316 self.logger = logger
1317 self.tempdir = tempdir
1318 self.vpp_pid = vpp_pid
1319 self.vpp_bin_path = vpp_bin_path
1320 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001321
1322
Damjan Marionf56b77a2016-10-03 19:44:57 +02001323class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001324 """
1325 @property result_string
1326 String variable to store the test case result string.
1327 @property errors
1328 List variable containing 2-tuples of TestCase instances and strings
1329 holding formatted tracebacks. Each tuple represents a test which
1330 raised an unexpected exception.
1331 @property failures
1332 List variable containing 2-tuples of TestCase instances and strings
1333 holding formatted tracebacks. Each tuple represents a test where
1334 a failure was explicitly signalled using the TestCase.assert*()
1335 methods.
1336 """
1337
juraj.linkes40dd73b2018-09-21 13:55:16 +02001338 failed_test_cases_info = set()
1339 core_crash_test_cases_info = set()
1340 current_test_case_info = None
1341
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001342 def __init__(self, stream=None, descriptions=None, verbosity=None,
1343 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001344 """
Klement Sekerada505f62017-01-04 12:58:53 +01001345 :param stream File descriptor to store where to report test results.
1346 Set to the standard error stream by default.
1347 :param descriptions Boolean variable to store information if to use
1348 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001349 :param verbosity Integer variable to store required verbosity level.
1350 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001351 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001352 self.stream = stream
1353 self.descriptions = descriptions
1354 self.verbosity = verbosity
1355 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001356 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001357 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001358
Damjan Marionf56b77a2016-10-03 19:44:57 +02001359 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001360 """
1361 Record a test succeeded result
1362
1363 :param test:
1364
1365 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001366 if self.current_test_case_info:
1367 self.current_test_case_info.logger.debug(
1368 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1369 test._testMethodName,
1370 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001371 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001372 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001373
juraj.linkescae64f82018-09-19 15:01:47 +02001374 self.send_result_through_pipe(test, PASS)
1375
Klement Sekeraf62ae122016-10-11 11:47:09 +02001376 def addSkip(self, test, reason):
1377 """
1378 Record a test skipped.
1379
1380 :param test:
1381 :param reason:
1382
1383 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001384 if self.current_test_case_info:
1385 self.current_test_case_info.logger.debug(
1386 "--- addSkip() %s.%s(%s) called, reason is %s" %
1387 (test.__class__.__name__, test._testMethodName,
1388 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001389 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001390 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001391
Klement Sekera558ceab2021-04-08 19:37:41 +02001392 if reason == "not enough cpus":
1393 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1394 else:
1395 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001396
juraj.linkes40dd73b2018-09-21 13:55:16 +02001397 def symlink_failed(self):
1398 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001399 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001400 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001401 link_path = os.path.join(
1402 failed_dir,
1403 '%s-FAILED' %
1404 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001405
1406 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001407 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001408 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001409 "os.symlink(%s, %s)" %
1410 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001411 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001412 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001413 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001414 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001415 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001416
Klement Sekeraf413bef2017-08-15 07:09:02 +02001417 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001418 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001419
juraj.linkescae64f82018-09-19 15:01:47 +02001420 def send_result_through_pipe(self, test, result):
1421 if hasattr(self, 'test_framework_result_pipe'):
1422 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001423 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001424 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001425
juraj.linkes40dd73b2018-09-21 13:55:16 +02001426 def log_error(self, test, err, fn_name):
1427 if self.current_test_case_info:
1428 if isinstance(test, unittest.suite._ErrorHolder):
1429 test_name = test.description
1430 else:
1431 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1432 test._testMethodName,
1433 test._testMethodDoc)
1434 self.current_test_case_info.logger.debug(
1435 "--- %s() %s called, err is %s" %
1436 (fn_name, test_name, err))
1437 self.current_test_case_info.logger.debug(
1438 "formatted exception is:\n%s" %
1439 "".join(format_exception(*err)))
1440
1441 def add_error(self, test, err, unittest_fn, error_type):
1442 if error_type == FAIL:
1443 self.log_error(test, err, 'addFailure')
1444 error_type_str = colorize("FAIL", RED)
1445 elif error_type == ERROR:
1446 self.log_error(test, err, 'addError')
1447 error_type_str = colorize("ERROR", RED)
1448 else:
1449 raise Exception('Error type %s cannot be used to record an '
1450 'error or a failure' % error_type)
1451
1452 unittest_fn(self, test, err)
1453 if self.current_test_case_info:
1454 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1455 (error_type_str,
1456 self.current_test_case_info.tempdir)
1457 self.symlink_failed()
1458 self.failed_test_cases_info.add(self.current_test_case_info)
1459 if is_core_present(self.current_test_case_info.tempdir):
1460 if not self.current_test_case_info.core_crash_test:
1461 if isinstance(test, unittest.suite._ErrorHolder):
1462 test_name = str(test)
1463 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001464 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001465 get_testcase_doc_name(test), test.id())
1466 self.current_test_case_info.core_crash_test = test_name
1467 self.core_crash_test_cases_info.add(
1468 self.current_test_case_info)
1469 else:
1470 self.result_string = '%s [no temp dir]' % error_type_str
1471
1472 self.send_result_through_pipe(test, error_type)
1473
Damjan Marionf56b77a2016-10-03 19:44:57 +02001474 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001475 """
1476 Record a test failed result
1477
1478 :param test:
1479 :param err: error message
1480
1481 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001482 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001483
Damjan Marionf56b77a2016-10-03 19:44:57 +02001484 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001485 """
1486 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001487
Klement Sekeraf62ae122016-10-11 11:47:09 +02001488 :param test:
1489 :param err: error message
1490
1491 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001492 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001493
Damjan Marionf56b77a2016-10-03 19:44:57 +02001494 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001495 """
1496 Get test description
1497
1498 :param test:
1499 :returns: test description
1500
1501 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001502 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001503
Damjan Marionf56b77a2016-10-03 19:44:57 +02001504 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001505 """
1506 Start a test
1507
1508 :param test:
1509
1510 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001511
1512 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001513 if test.__class__ in self.printed:
1514 return
1515
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001516 test_doc = getdoc(test)
1517 if not test_doc:
1518 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001519
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001520 test_title = test_doc.splitlines()[0]
Klement Sekera558ceab2021-04-08 19:37:41 +02001521 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001522 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001523 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001524
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001525 # This block may overwrite the colorized title above,
1526 # but we want this to stand out and be fixed
1527 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekera558ceab2021-04-08 19:37:41 +02001528 test_title = colorize(
1529 f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001530
Klement Sekera558ceab2021-04-08 19:37:41 +02001531 if test.__class__.skipped_due_to_cpu_lack:
1532 test_title = colorize(
1533 f"{test_title} [skipped - not enough cpus, "
1534 f"required={test.__class__.get_cpus_required()}, "
1535 f"available={max_vpp_cpus}]", YELLOW)
1536
1537 print(double_line_delim)
1538 print(test_title)
1539 print(double_line_delim)
1540 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001541
1542 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001543 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001544 unittest.TestResult.startTest(self, test)
1545 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001546 self.stream.writeln(
1547 "Starting " + self.getDescription(test) + " ...")
1548 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001549
Damjan Marionf56b77a2016-10-03 19:44:57 +02001550 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001551 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001552 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001553
1554 :param test:
1555
1556 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001557 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001558
Damjan Marionf56b77a2016-10-03 19:44:57 +02001559 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001560 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001561 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001562 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001563 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001564 else:
Ole Troan0c629322019-11-28 14:48:44 +01001565 self.stream.writeln("%-68s %4.2f %s" %
1566 (self.getDescription(test),
1567 time.time() - self.start_test,
1568 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001569
1570 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001571
Damjan Marionf56b77a2016-10-03 19:44:57 +02001572 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001573 """
1574 Print errors from running the test case
1575 """
juraj.linkesabec0122018-11-16 17:28:56 +01001576 if len(self.errors) > 0 or len(self.failures) > 0:
1577 self.stream.writeln()
1578 self.printErrorList('ERROR', self.errors)
1579 self.printErrorList('FAIL', self.failures)
1580
1581 # ^^ that is the last output from unittest before summary
1582 if not self.runner.print_summary:
1583 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1584 self.stream = devnull
1585 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001586
Damjan Marionf56b77a2016-10-03 19:44:57 +02001587 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001588 """
1589 Print error list to the output stream together with error type
1590 and test case description.
1591
1592 :param flavour: error type
1593 :param errors: iterable errors
1594
1595 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001596 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001597 self.stream.writeln(double_line_delim)
1598 self.stream.writeln("%s: %s" %
1599 (flavour, self.getDescription(test)))
1600 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001601 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001602
1603
Damjan Marionf56b77a2016-10-03 19:44:57 +02001604class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001605 """
Klement Sekera104543f2017-02-03 07:29:43 +01001606 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001607 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001608
Klement Sekeraf62ae122016-10-11 11:47:09 +02001609 @property
1610 def resultclass(self):
1611 """Class maintaining the results of the tests"""
1612 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001613
juraj.linkes184870a2018-07-16 14:22:01 +02001614 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001615 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001616 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001617 # ignore stream setting here, use hard-coded stdout to be in sync
1618 # with prints from VppTestCase methods ...
1619 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1620 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001621 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001622 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001623
juraj.linkesabec0122018-11-16 17:28:56 +01001624 self.orig_stream = self.stream
1625 self.resultclass.test_framework_result_pipe = result_pipe
1626
1627 self.print_summary = print_summary
1628
1629 def _makeResult(self):
1630 return self.resultclass(self.stream,
1631 self.descriptions,
1632 self.verbosity,
1633 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001634
Damjan Marionf56b77a2016-10-03 19:44:57 +02001635 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001636 """
1637 Run the tests
1638
1639 :param test:
1640
1641 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001642 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001643
1644 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001645 if not self.print_summary:
1646 self.stream = self.orig_stream
1647 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001648 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001649
1650
1651class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001652 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1653 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001654 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001655 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001656 if hasattr(self, 'testcase') and self.testcase.debug_all:
1657 if self.testcase.debug_gdbserver:
1658 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1659 .format(port=self.testcase.gdbserver_port)] + args
1660 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1661 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001662 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001663 self.app_name = os.path.basename(self.app_bin)
1664 if hasattr(self, 'role'):
1665 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001666 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001667 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001668 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001669 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001670
Dave Wallace24564332019-10-21 02:53:14 +00001671 def wait_for_enter(self):
1672 if not hasattr(self, 'testcase'):
1673 return
1674 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1675 print()
1676 print(double_line_delim)
1677 print("Spawned GDB Server for '{app}' with PID: {pid}"
1678 .format(app=self.app_name, pid=self.process.pid))
1679 elif self.testcase.debug_all and self.testcase.debug_gdb:
1680 print()
1681 print(double_line_delim)
1682 print("Spawned '{app}' with PID: {pid}"
1683 .format(app=self.app_name, pid=self.process.pid))
1684 else:
1685 return
1686 print(single_line_delim)
1687 print("You can debug '{app}' using:".format(app=self.app_name))
1688 if self.testcase.debug_gdbserver:
1689 print("sudo gdb " + self.app_bin +
1690 " -ex 'target remote localhost:{port}'"
1691 .format(port=self.testcase.gdbserver_port))
1692 print("Now is the time to attach gdb by running the above "
1693 "command, set up breakpoints etc., then resume from "
1694 "within gdb by issuing the 'continue' command")
1695 self.testcase.gdbserver_port += 1
1696 elif self.testcase.debug_gdb:
1697 print("sudo gdb " + self.app_bin +
1698 " -ex 'attach {pid}'".format(pid=self.process.pid))
1699 print("Now is the time to attach gdb by running the above "
1700 "command and set up breakpoints etc., then resume from"
1701 " within gdb by issuing the 'continue' command")
1702 print(single_line_delim)
1703 input("Press ENTER to continue running the testcase...")
1704
Neale Ranns812ed392017-10-16 04:20:13 -07001705 def run(self):
1706 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001707 if not os.path.exists(executable) or not os.access(
1708 executable, os.F_OK | os.X_OK):
1709 # Exit code that means some system file did not exist,
1710 # could not be opened, or had some other kind of error.
1711 self.result = os.EX_OSFILE
1712 raise EnvironmentError(
1713 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001714 self.logger.debug("Running executable: '{app}'"
1715 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001716 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001717 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001718 env["CK_LOG_FILE_NAME"] = "-"
1719 self.process = subprocess.Popen(
1720 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1721 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001722 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001723 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001724 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001725 self.logger.info("Return code is `%s'" % self.process.returncode)
1726 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001727 self.logger.info("Executable `{app}' wrote to stdout:"
1728 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001729 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001730 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001731 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001732 self.logger.info("Executable `{app}' wrote to stderr:"
1733 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001734 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001735 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001736 self.logger.info(single_line_delim)
1737 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001738
Klement Sekera6aa58b72019-05-16 14:34:55 +02001739
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001740if __name__ == '__main__':
1741 pass