blob: 4d0f45621b4ecca86b49bc0cf28d79e7ff6a8d20 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
4import gc
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05005import logging
Paul Vinciguerra72f00042018-11-25 11:05:13 -08006import sys
Ole Trøan162989e2018-11-26 10:27:50 +00007import os
8import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04009import signal
Paul Vinciguerrad6f22172020-12-05 22:39:14 +000010import subprocess
Ole Trøan162989e2018-11-26 10:27:50 +000011import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +020012import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020013import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080014import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000015import random
16import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080017import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010018import platform
Ole Trøan162989e2018-11-26 10:27:50 +000019from collections import deque
20from threading import Thread, Event
21from inspect import getdoc, isclass
22from traceback import format_exception
23from logging import FileHandler, DEBUG, Formatter
Andrew Yourtchenko06f32812021-01-14 10:19:08 +000024from enum import Enum
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070025
26import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000027from scapy.packet import Raw
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040028import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080029from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010030from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000031from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070032from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000033from vpp_papi_provider import VppPapiProvider
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050034import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000035from vpp_papi.vpp_stats import VPPStats
Paul Vinciguerra499ea642019-03-15 09:39:19 -070036from vpp_papi.vpp_transport_shmem import VppTransportShmemIOError
Ole Trøan162989e2018-11-26 10:27:50 +000037from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
38 get_logger, colorize
39from vpp_object import VppObjectRegistry
40from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020041from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
42from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
43from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080044
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050045logger = logging.getLogger(__name__)
46
47# Set up an empty logger for the testcase that can be overridden as necessary
48null_logger = logging.getLogger('VppTestCase')
49null_logger.addHandler(logging.NullHandler())
50
juraj.linkescae64f82018-09-19 15:01:47 +020051PASS = 0
52FAIL = 1
53ERROR = 2
54SKIP = 3
55TEST_RUN = 4
56
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040057
58class BoolEnvironmentVariable(object):
59
60 def __init__(self, env_var_name, default='n', true_values=None):
61 self.name = env_var_name
62 self.default = default
63 self.true_values = true_values if true_values is not None else \
64 ("y", "yes", "1")
65
66 def __bool__(self):
67 return os.getenv(self.name, self.default).lower() in self.true_values
68
69 if sys.version_info[0] == 2:
70 __nonzero__ = __bool__
71
72 def __repr__(self):
73 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
74 (self.name, self.default, self.true_values)
75
76
77debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
78if debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010079 import debug_internal
80
Klement Sekeraf62ae122016-10-11 11:47:09 +020081"""
82 Test framework module.
83
84 The module provides a set of tools for constructing and running tests and
85 representing the results.
86"""
87
Klement Sekeraf62ae122016-10-11 11:47:09 +020088
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040089class VppDiedError(Exception):
90 """ exception for reporting that the subprocess has died."""
91
92 signals_by_value = {v: k for k, v in signal.__dict__.items() if
93 k.startswith('SIG') and not k.startswith('SIG_')}
94
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040095 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040096 self.rv = rv
97 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040098 self.testcase = testcase
99 self.method_name = method_name
100
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400101 try:
102 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400103 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400104 pass
105
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400106 if testcase is None and method_name is None:
107 in_msg = ''
108 else:
109 in_msg = 'running %s.%s ' % (testcase, method_name)
110
111 msg = "VPP subprocess died %sunexpectedly with return code: %d%s." % (
112 in_msg,
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400113 self.rv,
Paul Vinciguerraf7457522019-07-13 09:35:38 -0400114 ' [%s]' % (self.signal_name if
115 self.signal_name is not None else ''))
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400116 super(VppDiedError, self).__init__(msg)
117
118
Damjan Marionf56b77a2016-10-03 19:44:57 +0200119class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200120 """Private class to create packet info object.
121
122 Help process information about the next packet.
123 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200124 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100125 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200126 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100127 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200128 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100129 #: Store the index of the destination packet generator interface
130 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200131 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100132 #: Store expected ip version
133 ip = -1
134 #: Store expected upper protocol
135 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100136 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200137 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200138
Matej Klotton16a14cd2016-12-07 15:09:13 +0100139 def __eq__(self, other):
140 index = self.index == other.index
141 src = self.src == other.src
142 dst = self.dst == other.dst
143 data = self.data == other.data
144 return index and src and dst and data
145
Klement Sekeraf62ae122016-10-11 11:47:09 +0200146
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100147def pump_output(testclass):
148 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100149 stdout_fragment = ""
150 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400151 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100152 readable = select.select([testclass.vpp.stdout.fileno(),
153 testclass.vpp.stderr.fileno(),
154 testclass.pump_thread_wakeup_pipe[0]],
155 [], [])[0]
156 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100157 read = os.read(testclass.vpp.stdout.fileno(), 102400)
158 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200159 split = read.decode('ascii',
160 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100161 if len(stdout_fragment) > 0:
162 split[0] = "%s%s" % (stdout_fragment, split[0])
163 if len(split) > 0 and split[-1].endswith("\n"):
164 limit = None
165 else:
166 limit = -1
167 stdout_fragment = split[-1]
168 testclass.vpp_stdout_deque.extend(split[:limit])
169 if not testclass.cache_vpp_output:
170 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100171 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100172 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100173 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100174 read = os.read(testclass.vpp.stderr.fileno(), 102400)
175 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200176 split = read.decode('ascii',
177 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100178 if len(stderr_fragment) > 0:
179 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200180 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100181 limit = None
182 else:
183 limit = -1
184 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200185
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100186 testclass.vpp_stderr_deque.extend(split[:limit])
187 if not testclass.cache_vpp_output:
188 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100189 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100190 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800191 # ignoring the dummy pipe here intentionally - the
192 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200193
194
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800195def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400196 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100197
Klement Sekera6aa58b72019-05-16 14:34:55 +0200198
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800199is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100200
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800201
202def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100203 return platform.machine() == 'aarch64'
204
Klement Sekera6aa58b72019-05-16 14:34:55 +0200205
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800206is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100207
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800208
209def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400210 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100211
Klement Sekera6aa58b72019-05-16 14:34:55 +0200212
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800213running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100214
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800215
Dave Barachd498c9e2020-03-10 16:59:39 -0400216def _running_gcov_tests():
217 return BoolEnvironmentVariable("GCOV_TESTS")
218
219
220running_gcov_tests = _running_gcov_tests()
221
222
Klement Sekera909a6a12017-08-08 04:33:53 +0200223class KeepAliveReporter(object):
224 """
225 Singleton object which reports test start to parent process
226 """
227 _shared_state = {}
228
229 def __init__(self):
230 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800231 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200232
233 @property
234 def pipe(self):
235 return self._pipe
236
237 @pipe.setter
238 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800239 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200240 raise Exception("Internal error - pipe should only be set once.")
241 self._pipe = pipe
242
juraj.linkes40dd73b2018-09-21 13:55:16 +0200243 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200244 """
245 Write current test tmpdir & desc to keep-alive pipe to signal liveness
246 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200247 if self.pipe is None:
248 # if not running forked..
249 return
250
Klement Sekera909a6a12017-08-08 04:33:53 +0200251 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200252 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200253 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200254 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200255
Dave Wallacee2efd122017-09-30 22:04:21 -0400256 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200257
258
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000259class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000260 # marks the suites that must run at the end
261 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000262 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000263 # marks the suites broken on VPP multi-worker
264 FIXME_VPP_WORKERS = 2
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000265
266
267def create_tag_decorator(e):
268 def decorator(cls):
269 try:
270 cls.test_tags.append(e)
271 except AttributeError:
272 cls.test_tags = [e]
273 return cls
274 return decorator
275
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000276
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000277tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000278tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000279
280
Damjan Marionf56b77a2016-10-03 19:44:57 +0200281class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100282 """This subclass is a base class for VPP test cases that are implemented as
283 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200284 """
285
Ole Troana45dc072018-12-21 16:04:22 +0100286 extra_vpp_punt_config = []
287 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500288 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400289 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100290
Klement Sekeraf62ae122016-10-11 11:47:09 +0200291 @property
292 def packet_infos(self):
293 """List of packet infos"""
294 return self._packet_infos
295
Klement Sekeradab231a2016-12-21 08:50:14 +0100296 @classmethod
297 def get_packet_count_for_if_idx(cls, dst_if_index):
298 """Get the number of packet info for specified destination if index"""
299 if dst_if_index in cls._packet_count_for_dst_if_idx:
300 return cls._packet_count_for_dst_if_idx[dst_if_index]
301 else:
302 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200303
304 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000305 def has_tag(cls, tag):
306 """ if the test case has a given tag - return true """
307 try:
308 return tag in cls.test_tags
309 except AttributeError:
310 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000311 return False
312
313 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000314 def is_tagged_run_solo(cls):
315 """ if the test case class is timing-sensitive - return true """
316 return cls.has_tag(TestCaseTag.RUN_SOLO)
317
318 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200319 def instance(cls):
320 """Return the instance of this testcase"""
321 return cls.test_instance
322
Damjan Marionf56b77a2016-10-03 19:44:57 +0200323 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200324 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000325 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200326 cls.debug_core = False
327 cls.debug_gdb = False
328 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000329 cls.debug_all = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200330 if d is None:
331 return
332 dl = d.lower()
333 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200334 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000335 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200336 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000337 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200338 cls.debug_gdbserver = True
339 else:
340 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000341 if dl == "gdb-all" or dl == "gdbserver-all":
342 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200343
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800344 @staticmethod
345 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200346 cpu_usage_list = [set(range(psutil.cpu_count()))]
347 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
348 if 'vpp_main' == p.info['name']]
349 for vpp_process in vpp_processes:
350 for cpu_usage_set in cpu_usage_list:
351 try:
352 cpu_num = vpp_process.cpu_num()
353 if cpu_num in cpu_usage_set:
354 cpu_usage_set_index = cpu_usage_list.index(
355 cpu_usage_set)
356 if cpu_usage_set_index == len(cpu_usage_list) - 1:
357 cpu_usage_list.append({cpu_num})
358 else:
359 cpu_usage_list[cpu_usage_set_index + 1].add(
360 cpu_num)
361 cpu_usage_set.remove(cpu_num)
362 break
363 except psutil.NoSuchProcess:
364 pass
365
366 for cpu_usage_set in cpu_usage_list:
367 if len(cpu_usage_set) > 0:
368 min_usage_set = cpu_usage_set
369 break
370
371 return random.choice(tuple(min_usage_set))
372
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800373 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200374 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200375 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400376 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100377 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400378 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100379 c = os.getenv("CACHE_OUTPUT", "1")
380 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200381 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100382 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
383 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400384 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100385 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
386 plugin_path = None
387 if cls.plugin_path is not None:
388 if cls.extern_plugin_path is not None:
389 plugin_path = "%s:%s" % (
390 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100391 else:
392 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100393 elif cls.extern_plugin_path is not None:
394 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100395 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100396 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100397 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100398 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100399 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100400 if size is not None:
401 coredump_size = "coredump-size %s" % size
402 if coredump_size is None:
403 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200404
Ole Troana45dc072018-12-21 16:04:22 +0100405 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000406 if not hasattr(cls, "worker_config"):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000407 cls.worker_config = os.getenv("VPP_WORKER_CONFIG", "")
408 if cls.worker_config != "":
409 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
410 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200411
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000412 default_variant = os.getenv("VARIANT")
413 if default_variant is not None:
414 default_variant = "defaults { %s 100 }" % default_variant
415 else:
416 default_variant = ""
417
Dave Barach77841402020-04-29 17:04:10 -0400418 api_fuzzing = os.getenv("API_FUZZ")
419 if api_fuzzing is None:
420 api_fuzzing = 'off'
421
Ole Troana45dc072018-12-21 16:04:22 +0100422 cls.vpp_cmdline = [cls.vpp_bin, "unix",
423 "{", "nodaemon", debug_cli, "full-coredump",
424 coredump_size, "runtime-dir", cls.tempdir, "}",
425 "api-trace", "{", "on", "}", "api-segment", "{",
426 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000427 "main-core", str(cpu_core_number),
428 cls.worker_config, "}",
Dave Barach4ed25982019-12-25 09:24:58 -0500429 "physmem", "{", "max-size", "32m", "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200430 "statseg", "{", "socket-name", cls.stats_sock, "}",
431 "socksvr", "{", "socket-name", cls.api_sock, "}",
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000432 "node { ", default_variant, "}",
Dave Barach77841402020-04-29 17:04:10 -0400433 "api-fuzz {", api_fuzzing, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200434 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100435 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200436 "}", "plugin", "rdma_plugin.so", "{", "disable",
Neale Ranns2b202bc2020-09-21 08:17:51 +0000437 "}", "plugin", "lisp_unittest_plugin.so", "{",
438 "enable",
Ole Troana45dc072018-12-21 16:04:22 +0100439 "}", "plugin", "unittest_plugin.so", "{", "enable",
440 "}"] + cls.extra_vpp_plugin_config + ["}", ]
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000441
Ole Troana45dc072018-12-21 16:04:22 +0100442 if cls.extra_vpp_punt_config is not None:
443 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100444 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100445 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400446 if cls.test_plugin_path is not None:
447 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
448
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100449 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
450 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200451
452 @classmethod
453 def wait_for_enter(cls):
454 if cls.debug_gdbserver:
455 print(double_line_delim)
456 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
457 elif cls.debug_gdb:
458 print(double_line_delim)
459 print("Spawned VPP with PID: %d" % cls.vpp.pid)
460 else:
461 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
462 return
463 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000464 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200465 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400466 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000467 " -ex 'target remote localhost:{port}'"
468 .format(port=cls.gdbserver_port))
469 print("Now is the time to attach gdb by running the above "
470 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200471 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000472 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200473 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400474 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000475 print("Now is the time to attach gdb by running the above "
476 "command and set up breakpoints etc., then resume VPP from"
477 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200478 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800479 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200480
481 @classmethod
482 def run_vpp(cls):
483 cmdline = cls.vpp_cmdline
484
485 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100486 gdbserver = '/usr/bin/gdbserver'
487 if not os.path.isfile(gdbserver) or \
488 not os.access(gdbserver, os.X_OK):
489 raise Exception("gdbserver binary '%s' does not exist or is "
490 "not executable" % gdbserver)
491
Dave Wallace24564332019-10-21 02:53:14 +0000492 cmdline = [gdbserver, 'localhost:{port}'
493 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200494 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
495
Klement Sekera931be3a2016-11-03 05:36:01 +0100496 try:
497 cls.vpp = subprocess.Popen(cmdline,
498 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100499 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800500 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800501 cls.logger.critical("Subprocess returned with non-0 return code: ("
502 "%s)", e.returncode)
503 raise
504 except OSError as e:
505 cls.logger.critical("Subprocess returned with OS error: "
506 "(%s) %s", e.errno, e.strerror)
507 raise
508 except Exception as e:
509 cls.logger.exception("Subprocess returned unexpected from "
510 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100511 raise
512
Klement Sekera277b89c2016-10-28 13:20:27 +0200513 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100514
Damjan Marionf56b77a2016-10-03 19:44:57 +0200515 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000516 def wait_for_coredump(cls):
517 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400518 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000519 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400520 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000521 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400522 ok = False
523 while time.time() < deadline:
524 cls.sleep(1)
525 size = curr_size
526 curr_size = os.path.getsize(corefile)
527 if size == curr_size:
528 ok = True
529 break
530 if not ok:
531 cls.logger.error("Timed out waiting for coredump to complete:"
532 " %s", corefile)
533 else:
534 cls.logger.error("Coredump complete: %s, size %d",
535 corefile, curr_size)
536
537 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200538 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200539 """
540 Perform class setup before running the testcase
541 Remove shared memory files, start vpp and connect the vpp-api
542 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800543 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100544 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100545 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000546 seed = os.environ["RND_SEED"]
547 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100548 if hasattr(cls, 'parallel_handler'):
549 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100550 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700551
Klement Sekeraf62ae122016-10-11 11:47:09 +0200552 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200553 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200554 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200555 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200556 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
557 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100558 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
559 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200560 cls.file_handler.setLevel(DEBUG)
561 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700562 cls.logger.debug("--- setUpClass() for %s called ---" %
563 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200564 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200565 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200566 cls.logger.info("Temporary dir is %s, shm prefix is %s",
567 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000568 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200569 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100570 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100571 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200572 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100573 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100574 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200575 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200576 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200577 # need to catch exceptions here because if we raise, then the cleanup
578 # doesn't get called and we might end with a zombie vpp
579 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200580 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200581 cls.reporter.send_keep_alive(cls, 'setUpClass')
582 VppTestResult.current_test_case_info = TestCaseInfo(
583 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100584 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100585 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100586 cls.pump_thread_stop_flag = Event()
587 cls.pump_thread_wakeup_pipe = os.pipe()
588 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100589 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100590 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200591 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400592 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200593 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400594 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100595 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400596 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100597 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400598 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100599 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200600 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200601 try:
602 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100603 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200604 cls.vpp_startup_failed = True
605 cls.logger.critical(
606 "VPP died shortly after startup, check the"
607 " output to standard error for possible cause")
608 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100609 try:
610 cls.vapi.connect()
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500611 except vpp_papi.VPPIOError as e:
612 cls.logger.debug("Exception connecting to vapi: %s" % e)
613 cls.vapi.disconnect()
614
Klement Sekera085f5c02016-11-24 01:59:16 +0100615 if cls.debug_gdbserver:
616 print(colorize("You're running VPP inside gdbserver but "
617 "VPP-API connection failed, did you forget "
618 "to 'continue' VPP from within gdb?", RED))
619 raise
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400620 except vpp_papi.VPPRuntimeError as e:
621 cls.logger.debug("%s" % e)
622 cls.quit()
623 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000624 except Exception as e:
625 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400626 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100627 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200628
Damjan Marionf56b77a2016-10-03 19:44:57 +0200629 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500630 def _debug_quit(cls):
631 if (cls.debug_gdbserver or cls.debug_gdb):
632 try:
633 cls.vpp.poll()
634
635 if cls.vpp.returncode is None:
636 print()
637 print(double_line_delim)
638 print("VPP or GDB server is still running")
639 print(single_line_delim)
640 input("When done debugging, press ENTER to kill the "
641 "process and finish running the testcase...")
642 except AttributeError:
643 pass
644
645 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200646 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200647 """
648 Disconnect vpp-api, kill vpp and cleanup shared memory files
649 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500650 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200651
juraj.linkes184870a2018-07-16 14:22:01 +0200652 # first signal that we want to stop the pump thread, then wake it up
653 if hasattr(cls, 'pump_thread_stop_flag'):
654 cls.pump_thread_stop_flag.set()
655 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100656 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100657 if hasattr(cls, 'pump_thread'):
658 cls.logger.debug("Waiting for pump thread to stop")
659 cls.pump_thread.join()
660 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500661 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100662 cls.vpp_stderr_reader_thread.join()
663
Klement Sekeraf62ae122016-10-11 11:47:09 +0200664 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100665 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100666 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700667 cls.logger.debug("Disconnecting class vapi client on %s",
668 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100669 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700670 cls.logger.debug("Deleting class vapi attribute on %s",
671 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100672 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200673 cls.vpp.poll()
674 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000675 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100676 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400677 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100678 cls.logger.debug("Waiting for vpp to die")
679 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700680 cls.logger.debug("Deleting class vpp attribute on %s",
681 cls.__name__)
Paul Vinciguerrad1f05f72020-12-16 21:03:16 -0500682 cls.vpp.stdout.close()
683 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200684 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200685
Klement Sekera3747c752017-04-10 06:30:17 +0200686 if cls.vpp_startup_failed:
687 stdout_log = cls.logger.info
688 stderr_log = cls.logger.critical
689 else:
690 stdout_log = cls.logger.info
691 stderr_log = cls.logger.info
692
Klement Sekerae4504c62016-12-08 10:16:41 +0100693 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200694 stdout_log(single_line_delim)
695 stdout_log('VPP output to stdout while running %s:', cls.__name__)
696 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100697 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200698 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
699 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200700 stdout_log('\n%s', vpp_output)
701 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200702
Klement Sekerae4504c62016-12-08 10:16:41 +0100703 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200704 stderr_log(single_line_delim)
705 stderr_log('VPP output to stderr while running %s:', cls.__name__)
706 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100707 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200708 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
709 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200710 stderr_log('\n%s', vpp_output)
711 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200712
Damjan Marionf56b77a2016-10-03 19:44:57 +0200713 @classmethod
714 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200715 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700716 cls.logger.debug("--- tearDownClass() for %s called ---" %
717 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200718 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200719 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200720 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100721 cls.reset_packet_infos()
722 if debug_framework:
723 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200724
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700725 def show_commands_at_teardown(self):
726 """ Allow subclass specific teardown logging additions."""
727 self.logger.info("--- No test specific show commands provided. ---")
728
Damjan Marionf56b77a2016-10-03 19:44:57 +0200729 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200730 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100731 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
732 (self.__class__.__name__, self._testMethodName,
733 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700734
735 try:
736 if not self.vpp_dead:
737 self.logger.debug(self.vapi.cli("show trace max 1000"))
738 self.logger.info(self.vapi.ppcli("show interface"))
739 self.logger.info(self.vapi.ppcli("show hardware"))
740 self.logger.info(self.statistics.set_errors_str())
741 self.logger.info(self.vapi.ppcli("show run"))
742 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400743 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700744 self.logger.info("Logging testcase specific show commands.")
745 self.show_commands_at_teardown()
746 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500747 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000748 m = self._testMethodName
749 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500750 tmp_api_trace = "/tmp/%s" % api_trace
751 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
752 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
753 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
754 vpp_api_trace_log))
755 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500756 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500757 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700758 except VppTransportShmemIOError:
759 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
760 "Cannot log show commands.")
761 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100762 else:
763 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200764
Damjan Marionf56b77a2016-10-03 19:44:57 +0200765 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200766 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800767 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200768 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100769 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400770
771 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
772 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100773 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100774 self.vpp_stdout_deque.append(
775 "--- test setUp() for %s.%s(%s) starts here ---\n" %
776 (self.__class__.__name__, self._testMethodName,
777 self._testMethodDoc))
778 self.vpp_stderr_deque.append(
779 "--- test setUp() for %s.%s(%s) starts here ---\n" %
780 (self.__class__.__name__, self._testMethodName,
781 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200782 self.vapi.cli("clear trace")
783 # store the test instance inside the test class - so that objects
784 # holding the class can access instance methods (like assertEqual)
785 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200786
Damjan Marionf56b77a2016-10-03 19:44:57 +0200787 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200788 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200789 """
790 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200791
Klement Sekera75e7d132017-09-20 08:26:30 +0200792 :param interfaces: iterable interface indexes (if None,
793 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200794
Klement Sekeraf62ae122016-10-11 11:47:09 +0200795 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200796 if interfaces is None:
797 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200798 for i in interfaces:
799 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200800
Damjan Marionf56b77a2016-10-03 19:44:57 +0200801 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100802 def register_capture(cls, cap_name):
803 """ Register a capture in the testclass """
804 # add to the list of captures with current timestamp
805 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100806
807 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000808 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400809 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
810 # returns float("2.190522")
811 timestr = cls.vapi.cli('show clock')
812 head, sep, tail = timestr.partition(',')
813 head, sep, tail = head.partition('Time now')
814 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000815
816 @classmethod
817 def sleep_on_vpp_time(cls, sec):
818 """ Sleep according to time in VPP world """
819 # On a busy system with many processes
820 # we might end up with VPP time being slower than real world
821 # So take that into account when waiting for VPP to do something
822 start_time = cls.get_vpp_time()
823 while cls.get_vpp_time() - start_time < sec:
824 cls.sleep(0.1)
825
826 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100827 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000828 """ Enable the PG, wait till it is done, then clean up """
Benoît Ganne8c45e512021-02-19 16:39:13 +0100829 if trace:
830 cls.vapi.cli("clear trace")
831 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200832 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000833 # PG, when starts, runs to completion -
834 # so let's avoid a race condition,
835 # and wait a little till it's done.
836 # Then clean it up - and then be gone.
837 deadline = time.time() + 300
838 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
839 cls.sleep(0.01) # yield
840 if time.time() > deadline:
841 cls.logger.error("Timeout waiting for pg to stop")
842 break
843 for stamp, cap_name in cls._captures:
844 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100845 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200846
Damjan Marionf56b77a2016-10-03 19:44:57 +0200847 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200848 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200849 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100850 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200851
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100852 :param interfaces: iterable indexes of the interfaces.
853 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200854
Klement Sekeraf62ae122016-10-11 11:47:09 +0200855 """
856 result = []
857 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200858 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200859 setattr(cls, intf.name, intf)
860 result.append(intf)
861 cls.pg_interfaces = result
862 return result
863
Matej Klotton0178d522016-11-04 11:11:44 +0100864 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200865 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100866 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100867 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100868
Klement Sekerab9ef2732018-06-24 22:49:33 +0200869 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100870 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100871 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200872 result = [VppLoInterface(cls) for i in range(count)]
873 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100874 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100875 cls.lo_interfaces = result
876 return result
877
Neale Ranns192b13f2019-03-15 02:16:20 -0700878 @classmethod
879 def create_bvi_interfaces(cls, count):
880 """
881 Create BVI interfaces.
882
883 :param count: number of interfaces created.
884 :returns: List of created interfaces.
885 """
886 result = [VppBviInterface(cls) for i in range(count)]
887 for intf in result:
888 setattr(cls, intf.name, intf)
889 cls.bvi_interfaces = result
890 return result
891
Damjan Marionf56b77a2016-10-03 19:44:57 +0200892 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200893 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200894 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200895 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200896 NOTE: Currently works only when Raw layer is present.
897
898 :param packet: packet
899 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200900 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200901
902 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200903 packet_len = len(packet) + 4
904 extend = size - packet_len
905 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200906 num = (extend // len(padding)) + 1
907 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200908
Klement Sekeradab231a2016-12-21 08:50:14 +0100909 @classmethod
910 def reset_packet_infos(cls):
911 """ Reset the list of packet info objects and packet counts to zero """
912 cls._packet_infos = {}
913 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200914
Klement Sekeradab231a2016-12-21 08:50:14 +0100915 @classmethod
916 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200917 """
918 Create packet info object containing the source and destination indexes
919 and add it to the testcase's packet info list
920
Klement Sekeradab231a2016-12-21 08:50:14 +0100921 :param VppInterface src_if: source interface
922 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200923
924 :returns: _PacketInfo object
925
926 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200927 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100928 info.index = len(cls._packet_infos)
929 info.src = src_if.sw_if_index
930 info.dst = dst_if.sw_if_index
931 if isinstance(dst_if, VppSubInterface):
932 dst_idx = dst_if.parent.sw_if_index
933 else:
934 dst_idx = dst_if.sw_if_index
935 if dst_idx in cls._packet_count_for_dst_if_idx:
936 cls._packet_count_for_dst_if_idx[dst_idx] += 1
937 else:
938 cls._packet_count_for_dst_if_idx[dst_idx] = 1
939 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200940 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200941
Damjan Marionf56b77a2016-10-03 19:44:57 +0200942 @staticmethod
943 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200944 """
945 Convert _PacketInfo object to packet payload
946
947 :param info: _PacketInfo object
948
949 :returns: string containing serialized data from packet info
950 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100951 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
952 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200953
Damjan Marionf56b77a2016-10-03 19:44:57 +0200954 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800955 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200956 """
957 Convert packet payload to _PacketInfo object
958
959 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700960 :type payload: <class 'scapy.packet.Raw'>
961 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800962 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700963 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200964 :returns: _PacketInfo object containing de-serialized data from payload
965
966 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800967 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200968 info = _PacketInfo()
969 info.index = int(numbers[0])
970 info.src = int(numbers[1])
971 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100972 info.ip = int(numbers[3])
973 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200974 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200975
Damjan Marionf56b77a2016-10-03 19:44:57 +0200976 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200977 """
978 Iterate over the packet info list stored in the testcase
979 Start iteration with first element if info is None
980 Continue based on index in info if info is specified
981
982 :param info: info or None
983 :returns: next info in list or None if no more infos
984 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200985 if info is None:
986 next_index = 0
987 else:
988 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100989 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200990 return None
991 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100992 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200993
Klement Sekeraf62ae122016-10-11 11:47:09 +0200994 def get_next_packet_info_for_interface(self, src_index, info):
995 """
996 Search the packet info list for the next packet info with same source
997 interface index
998
999 :param src_index: source interface index to search for
1000 :param info: packet info - where to start the search
1001 :returns: packet info or None
1002
1003 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001004 while True:
1005 info = self.get_next_packet_info(info)
1006 if info is None:
1007 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001008 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001009 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001010
Klement Sekeraf62ae122016-10-11 11:47:09 +02001011 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1012 """
1013 Search the packet info list for the next packet info with same source
1014 and destination interface indexes
1015
1016 :param src_index: source interface index to search for
1017 :param dst_index: destination interface index to search for
1018 :param info: packet info - where to start the search
1019 :returns: packet info or None
1020
1021 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001022 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001023 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001024 if info is None:
1025 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001026 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001027 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001028
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001029 def assert_equal(self, real_value, expected_value, name_or_class=None):
1030 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001031 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001032 return
1033 try:
1034 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1035 msg = msg % (getdoc(name_or_class).strip(),
1036 real_value, str(name_or_class(real_value)),
1037 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001038 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001039 msg = "Invalid %s: %s does not match expected value %s" % (
1040 name_or_class, real_value, expected_value)
1041
1042 self.assertEqual(real_value, expected_value, msg)
1043
Klement Sekerab17dd962017-01-09 07:43:48 +01001044 def assert_in_range(self,
1045 real_value,
1046 expected_min,
1047 expected_max,
1048 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001049 if name is None:
1050 msg = None
1051 else:
1052 msg = "Invalid %s: %s out of range <%s,%s>" % (
1053 name, real_value, expected_min, expected_max)
1054 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1055
Klement Sekerad81ae412018-05-16 10:52:54 +02001056 def assert_packet_checksums_valid(self, packet,
1057 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001058 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001059 udp_layers = ['UDP', 'UDPerror']
1060 checksum_fields = ['cksum', 'chksum']
1061 checksums = []
1062 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001063 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001064 while True:
1065 layer = temp.getlayer(counter)
1066 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001067 layer = layer.copy()
1068 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001069 for cf in checksum_fields:
1070 if hasattr(layer, cf):
1071 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001072 0 == getattr(layer, cf) and \
1073 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001074 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001075 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001076 checksums.append((counter, cf))
1077 else:
1078 break
1079 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001080 if 0 == len(checksums):
1081 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001082 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001083 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001084 calc_sum = getattr(temp[layer], cf)
1085 self.assert_equal(
1086 getattr(received[layer], cf), calc_sum,
1087 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1088 self.logger.debug(
1089 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1090 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001091
1092 def assert_checksum_valid(self, received_packet, layer,
1093 field_name='chksum',
1094 ignore_zero_checksum=False):
1095 """ Check checksum of received packet on given layer """
1096 received_packet_checksum = getattr(received_packet[layer], field_name)
1097 if ignore_zero_checksum and 0 == received_packet_checksum:
1098 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001099 recalculated = received_packet.__class__(
1100 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001101 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001102 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001103 self.assert_equal(received_packet_checksum,
1104 getattr(recalculated[layer], field_name),
1105 "packet checksum on layer: %s" % layer)
1106
1107 def assert_ip_checksum_valid(self, received_packet,
1108 ignore_zero_checksum=False):
1109 self.assert_checksum_valid(received_packet, 'IP',
1110 ignore_zero_checksum=ignore_zero_checksum)
1111
1112 def assert_tcp_checksum_valid(self, received_packet,
1113 ignore_zero_checksum=False):
1114 self.assert_checksum_valid(received_packet, 'TCP',
1115 ignore_zero_checksum=ignore_zero_checksum)
1116
1117 def assert_udp_checksum_valid(self, received_packet,
1118 ignore_zero_checksum=True):
1119 self.assert_checksum_valid(received_packet, 'UDP',
1120 ignore_zero_checksum=ignore_zero_checksum)
1121
1122 def assert_embedded_icmp_checksum_valid(self, received_packet):
1123 if received_packet.haslayer(IPerror):
1124 self.assert_checksum_valid(received_packet, 'IPerror')
1125 if received_packet.haslayer(TCPerror):
1126 self.assert_checksum_valid(received_packet, 'TCPerror')
1127 if received_packet.haslayer(UDPerror):
1128 self.assert_checksum_valid(received_packet, 'UDPerror',
1129 ignore_zero_checksum=True)
1130 if received_packet.haslayer(ICMPerror):
1131 self.assert_checksum_valid(received_packet, 'ICMPerror')
1132
1133 def assert_icmp_checksum_valid(self, received_packet):
1134 self.assert_checksum_valid(received_packet, 'ICMP')
1135 self.assert_embedded_icmp_checksum_valid(received_packet)
1136
1137 def assert_icmpv6_checksum_valid(self, pkt):
1138 if pkt.haslayer(ICMPv6DestUnreach):
1139 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1140 self.assert_embedded_icmp_checksum_valid(pkt)
1141 if pkt.haslayer(ICMPv6EchoRequest):
1142 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1143 if pkt.haslayer(ICMPv6EchoReply):
1144 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1145
Klement Sekera3a343d42019-05-16 14:35:46 +02001146 def get_packet_counter(self, counter):
1147 if counter.startswith("/"):
1148 counter_value = self.statistics.get_counter(counter)
1149 else:
1150 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001151 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001152 for i in range(1, len(counters) - 1):
1153 results = counters[i].split()
1154 if results[1] == counter:
1155 counter_value = int(results[0])
1156 break
1157 return counter_value
1158
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001159 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001160 counter_value = self.get_packet_counter(counter)
1161 self.assert_equal(counter_value, expected_value,
1162 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001163
Ole Troan233e4682019-05-16 15:01:34 +02001164 def assert_error_counter_equal(self, counter, expected_value):
1165 counter_value = self.statistics.get_err_counter(counter)
1166 self.assert_equal(counter_value, expected_value,
1167 "error counter `%s'" % counter)
1168
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001169 @classmethod
1170 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001171
1172 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1173 # * by Guido, only the main thread can be interrupted.
1174 # */
1175 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1176 if timeout == 0:
1177 # yield quantum
1178 if hasattr(os, 'sched_yield'):
1179 os.sched_yield()
1180 else:
1181 time.sleep(0)
1182 return
1183
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001184 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001185 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001186 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001187 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001188 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001189 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001190 "slept for %es instead of ~%es!",
1191 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001192
1193 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001194 "Finished sleep (%s) - slept %es (wanted %es)",
1195 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001196
Benoît Ganne8c45e512021-02-19 16:39:13 +01001197 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001198 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001199 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001200 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001201
1202 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1203 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001204 if not timeout:
1205 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001206 for i in self.pg_interfaces:
1207 i.get_capture(0, timeout=timeout)
1208 i.assert_nothing_captured(remark=remark)
1209 timeout = 0.1
1210
Benoît Ganne8c45e512021-02-19 16:39:13 +01001211 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1212 trace=True):
Neale Rannsd7603d92019-03-28 08:56:10 +00001213 if not n_rx:
1214 n_rx = len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001215 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001216 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001217 return rx
1218
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001219 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1220 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001221 rx = output.get_capture(len(pkts))
1222 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001223 if not timeout:
1224 timeout = 1
1225 for i in self.pg_interfaces:
1226 if i not in outputs:
1227 i.get_capture(0, timeout=timeout)
1228 i.assert_nothing_captured()
1229 timeout = 0.1
1230
Neale Ranns52fae862018-01-08 04:41:42 -08001231 return rx
1232
Damjan Marionf56b77a2016-10-03 19:44:57 +02001233
juraj.linkes184870a2018-07-16 14:22:01 +02001234def get_testcase_doc_name(test):
1235 return getdoc(test.__class__).splitlines()[0]
1236
1237
Ole Trøan5ba91592018-11-22 10:01:09 +00001238def get_test_description(descriptions, test):
1239 short_description = test.shortDescription()
1240 if descriptions and short_description:
1241 return short_description
1242 else:
1243 return str(test)
1244
1245
juraj.linkes40dd73b2018-09-21 13:55:16 +02001246class TestCaseInfo(object):
1247 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1248 self.logger = logger
1249 self.tempdir = tempdir
1250 self.vpp_pid = vpp_pid
1251 self.vpp_bin_path = vpp_bin_path
1252 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001253
1254
Damjan Marionf56b77a2016-10-03 19:44:57 +02001255class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001256 """
1257 @property result_string
1258 String variable to store the test case result string.
1259 @property errors
1260 List variable containing 2-tuples of TestCase instances and strings
1261 holding formatted tracebacks. Each tuple represents a test which
1262 raised an unexpected exception.
1263 @property failures
1264 List variable containing 2-tuples of TestCase instances and strings
1265 holding formatted tracebacks. Each tuple represents a test where
1266 a failure was explicitly signalled using the TestCase.assert*()
1267 methods.
1268 """
1269
juraj.linkes40dd73b2018-09-21 13:55:16 +02001270 failed_test_cases_info = set()
1271 core_crash_test_cases_info = set()
1272 current_test_case_info = None
1273
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001274 def __init__(self, stream=None, descriptions=None, verbosity=None,
1275 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001276 """
Klement Sekerada505f62017-01-04 12:58:53 +01001277 :param stream File descriptor to store where to report test results.
1278 Set to the standard error stream by default.
1279 :param descriptions Boolean variable to store information if to use
1280 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001281 :param verbosity Integer variable to store required verbosity level.
1282 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001283 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001284 self.stream = stream
1285 self.descriptions = descriptions
1286 self.verbosity = verbosity
1287 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001288 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001289
Damjan Marionf56b77a2016-10-03 19:44:57 +02001290 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001291 """
1292 Record a test succeeded result
1293
1294 :param test:
1295
1296 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001297 if self.current_test_case_info:
1298 self.current_test_case_info.logger.debug(
1299 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1300 test._testMethodName,
1301 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001302 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001303 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001304
juraj.linkescae64f82018-09-19 15:01:47 +02001305 self.send_result_through_pipe(test, PASS)
1306
Klement Sekeraf62ae122016-10-11 11:47:09 +02001307 def addSkip(self, test, reason):
1308 """
1309 Record a test skipped.
1310
1311 :param test:
1312 :param reason:
1313
1314 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001315 if self.current_test_case_info:
1316 self.current_test_case_info.logger.debug(
1317 "--- addSkip() %s.%s(%s) called, reason is %s" %
1318 (test.__class__.__name__, test._testMethodName,
1319 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001320 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001321 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001322
juraj.linkescae64f82018-09-19 15:01:47 +02001323 self.send_result_through_pipe(test, SKIP)
1324
juraj.linkes40dd73b2018-09-21 13:55:16 +02001325 def symlink_failed(self):
1326 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001327 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001328 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001329 link_path = os.path.join(
1330 failed_dir,
1331 '%s-FAILED' %
1332 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001333
1334 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001335 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001336 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001337 "os.symlink(%s, %s)" %
1338 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001339 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001340 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001341 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001342 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001343 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001344
Klement Sekeraf413bef2017-08-15 07:09:02 +02001345 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001346 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001347
juraj.linkescae64f82018-09-19 15:01:47 +02001348 def send_result_through_pipe(self, test, result):
1349 if hasattr(self, 'test_framework_result_pipe'):
1350 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001351 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001352 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001353
juraj.linkes40dd73b2018-09-21 13:55:16 +02001354 def log_error(self, test, err, fn_name):
1355 if self.current_test_case_info:
1356 if isinstance(test, unittest.suite._ErrorHolder):
1357 test_name = test.description
1358 else:
1359 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1360 test._testMethodName,
1361 test._testMethodDoc)
1362 self.current_test_case_info.logger.debug(
1363 "--- %s() %s called, err is %s" %
1364 (fn_name, test_name, err))
1365 self.current_test_case_info.logger.debug(
1366 "formatted exception is:\n%s" %
1367 "".join(format_exception(*err)))
1368
1369 def add_error(self, test, err, unittest_fn, error_type):
1370 if error_type == FAIL:
1371 self.log_error(test, err, 'addFailure')
1372 error_type_str = colorize("FAIL", RED)
1373 elif error_type == ERROR:
1374 self.log_error(test, err, 'addError')
1375 error_type_str = colorize("ERROR", RED)
1376 else:
1377 raise Exception('Error type %s cannot be used to record an '
1378 'error or a failure' % error_type)
1379
1380 unittest_fn(self, test, err)
1381 if self.current_test_case_info:
1382 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1383 (error_type_str,
1384 self.current_test_case_info.tempdir)
1385 self.symlink_failed()
1386 self.failed_test_cases_info.add(self.current_test_case_info)
1387 if is_core_present(self.current_test_case_info.tempdir):
1388 if not self.current_test_case_info.core_crash_test:
1389 if isinstance(test, unittest.suite._ErrorHolder):
1390 test_name = str(test)
1391 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001392 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001393 get_testcase_doc_name(test), test.id())
1394 self.current_test_case_info.core_crash_test = test_name
1395 self.core_crash_test_cases_info.add(
1396 self.current_test_case_info)
1397 else:
1398 self.result_string = '%s [no temp dir]' % error_type_str
1399
1400 self.send_result_through_pipe(test, error_type)
1401
Damjan Marionf56b77a2016-10-03 19:44:57 +02001402 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001403 """
1404 Record a test failed result
1405
1406 :param test:
1407 :param err: error message
1408
1409 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001410 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001411
Damjan Marionf56b77a2016-10-03 19:44:57 +02001412 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001413 """
1414 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001415
Klement Sekeraf62ae122016-10-11 11:47:09 +02001416 :param test:
1417 :param err: error message
1418
1419 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001420 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001421
Damjan Marionf56b77a2016-10-03 19:44:57 +02001422 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001423 """
1424 Get test description
1425
1426 :param test:
1427 :returns: test description
1428
1429 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001430 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001431
Damjan Marionf56b77a2016-10-03 19:44:57 +02001432 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001433 """
1434 Start a test
1435
1436 :param test:
1437
1438 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001439
1440 def print_header(test):
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001441 test_doc = getdoc(test)
1442 if not test_doc:
1443 raise Exception("No doc string for test '%s'" % test.id())
1444 test_title = test_doc.splitlines()[0]
1445 test_title_colored = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001446 if test.is_tagged_run_solo():
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001447 # long live PEP-8 and 80 char width limitation...
1448 c = YELLOW
1449 test_title_colored = colorize("SOLO RUN: " + test_title, c)
1450
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001451 # This block may overwrite the colorized title above,
1452 # but we want this to stand out and be fixed
1453 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
1454 c = RED
1455 w = "FIXME with VPP workers: "
1456 test_title_colored = colorize(w + test_title, c)
1457
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001458 if not hasattr(test.__class__, '_header_printed'):
1459 print(double_line_delim)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001460 print(test_title_colored)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001461 print(double_line_delim)
1462 test.__class__._header_printed = True
1463
1464 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001465 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001466 unittest.TestResult.startTest(self, test)
1467 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001468 self.stream.writeln(
1469 "Starting " + self.getDescription(test) + " ...")
1470 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001471
Damjan Marionf56b77a2016-10-03 19:44:57 +02001472 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001473 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001474 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001475
1476 :param test:
1477
1478 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001479 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001480
Damjan Marionf56b77a2016-10-03 19:44:57 +02001481 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001482 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001483 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001484 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001485 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001486 else:
Ole Troan0c629322019-11-28 14:48:44 +01001487 self.stream.writeln("%-68s %4.2f %s" %
1488 (self.getDescription(test),
1489 time.time() - self.start_test,
1490 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001491
1492 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001493
Damjan Marionf56b77a2016-10-03 19:44:57 +02001494 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001495 """
1496 Print errors from running the test case
1497 """
juraj.linkesabec0122018-11-16 17:28:56 +01001498 if len(self.errors) > 0 or len(self.failures) > 0:
1499 self.stream.writeln()
1500 self.printErrorList('ERROR', self.errors)
1501 self.printErrorList('FAIL', self.failures)
1502
1503 # ^^ that is the last output from unittest before summary
1504 if not self.runner.print_summary:
1505 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1506 self.stream = devnull
1507 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001508
Damjan Marionf56b77a2016-10-03 19:44:57 +02001509 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001510 """
1511 Print error list to the output stream together with error type
1512 and test case description.
1513
1514 :param flavour: error type
1515 :param errors: iterable errors
1516
1517 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001518 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001519 self.stream.writeln(double_line_delim)
1520 self.stream.writeln("%s: %s" %
1521 (flavour, self.getDescription(test)))
1522 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001523 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001524
1525
Damjan Marionf56b77a2016-10-03 19:44:57 +02001526class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001527 """
Klement Sekera104543f2017-02-03 07:29:43 +01001528 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001529 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001530
Klement Sekeraf62ae122016-10-11 11:47:09 +02001531 @property
1532 def resultclass(self):
1533 """Class maintaining the results of the tests"""
1534 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001535
juraj.linkes184870a2018-07-16 14:22:01 +02001536 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001537 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001538 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001539 # ignore stream setting here, use hard-coded stdout to be in sync
1540 # with prints from VppTestCase methods ...
1541 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1542 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001543 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001544 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001545
juraj.linkesabec0122018-11-16 17:28:56 +01001546 self.orig_stream = self.stream
1547 self.resultclass.test_framework_result_pipe = result_pipe
1548
1549 self.print_summary = print_summary
1550
1551 def _makeResult(self):
1552 return self.resultclass(self.stream,
1553 self.descriptions,
1554 self.verbosity,
1555 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001556
Damjan Marionf56b77a2016-10-03 19:44:57 +02001557 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001558 """
1559 Run the tests
1560
1561 :param test:
1562
1563 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001564 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001565
1566 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001567 if not self.print_summary:
1568 self.stream = self.orig_stream
1569 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001570 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001571
1572
1573class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001574 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1575 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001576 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001577 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001578 if hasattr(self, 'testcase') and self.testcase.debug_all:
1579 if self.testcase.debug_gdbserver:
1580 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1581 .format(port=self.testcase.gdbserver_port)] + args
1582 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1583 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001584 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001585 self.app_name = os.path.basename(self.app_bin)
1586 if hasattr(self, 'role'):
1587 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001588 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001589 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001590 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001591 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001592
Dave Wallace24564332019-10-21 02:53:14 +00001593 def wait_for_enter(self):
1594 if not hasattr(self, 'testcase'):
1595 return
1596 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1597 print()
1598 print(double_line_delim)
1599 print("Spawned GDB Server for '{app}' with PID: {pid}"
1600 .format(app=self.app_name, pid=self.process.pid))
1601 elif self.testcase.debug_all and self.testcase.debug_gdb:
1602 print()
1603 print(double_line_delim)
1604 print("Spawned '{app}' with PID: {pid}"
1605 .format(app=self.app_name, pid=self.process.pid))
1606 else:
1607 return
1608 print(single_line_delim)
1609 print("You can debug '{app}' using:".format(app=self.app_name))
1610 if self.testcase.debug_gdbserver:
1611 print("sudo gdb " + self.app_bin +
1612 " -ex 'target remote localhost:{port}'"
1613 .format(port=self.testcase.gdbserver_port))
1614 print("Now is the time to attach gdb by running the above "
1615 "command, set up breakpoints etc., then resume from "
1616 "within gdb by issuing the 'continue' command")
1617 self.testcase.gdbserver_port += 1
1618 elif self.testcase.debug_gdb:
1619 print("sudo gdb " + self.app_bin +
1620 " -ex 'attach {pid}'".format(pid=self.process.pid))
1621 print("Now is the time to attach gdb by running the above "
1622 "command and set up breakpoints etc., then resume from"
1623 " within gdb by issuing the 'continue' command")
1624 print(single_line_delim)
1625 input("Press ENTER to continue running the testcase...")
1626
Neale Ranns812ed392017-10-16 04:20:13 -07001627 def run(self):
1628 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001629 if not os.path.exists(executable) or not os.access(
1630 executable, os.F_OK | os.X_OK):
1631 # Exit code that means some system file did not exist,
1632 # could not be opened, or had some other kind of error.
1633 self.result = os.EX_OSFILE
1634 raise EnvironmentError(
1635 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001636 self.logger.debug("Running executable: '{app}'"
1637 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001638 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001639 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001640 env["CK_LOG_FILE_NAME"] = "-"
1641 self.process = subprocess.Popen(
1642 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1643 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001644 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001645 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001646 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001647 self.logger.info("Return code is `%s'" % self.process.returncode)
1648 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001649 self.logger.info("Executable `{app}' wrote to stdout:"
1650 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001651 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001652 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001653 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001654 self.logger.info("Executable `{app}' wrote to stderr:"
1655 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001656 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001657 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001658 self.logger.info(single_line_delim)
1659 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001660
Klement Sekera6aa58b72019-05-16 14:34:55 +02001661
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001662if __name__ == '__main__':
1663 pass