blob: c9ecafd2bcabc1719f66eac9fd636eea2955a98f [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__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200682 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200683
Klement Sekera3747c752017-04-10 06:30:17 +0200684 if cls.vpp_startup_failed:
685 stdout_log = cls.logger.info
686 stderr_log = cls.logger.critical
687 else:
688 stdout_log = cls.logger.info
689 stderr_log = cls.logger.info
690
Klement Sekerae4504c62016-12-08 10:16:41 +0100691 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200692 stdout_log(single_line_delim)
693 stdout_log('VPP output to stdout while running %s:', cls.__name__)
694 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100695 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200696 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
697 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200698 stdout_log('\n%s', vpp_output)
699 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200700
Klement Sekerae4504c62016-12-08 10:16:41 +0100701 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200702 stderr_log(single_line_delim)
703 stderr_log('VPP output to stderr while running %s:', cls.__name__)
704 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100705 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200706 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
707 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200708 stderr_log('\n%s', vpp_output)
709 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200710
Damjan Marionf56b77a2016-10-03 19:44:57 +0200711 @classmethod
712 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200713 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700714 cls.logger.debug("--- tearDownClass() for %s called ---" %
715 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200716 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200717 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200718 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100719 cls.reset_packet_infos()
720 if debug_framework:
721 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200722
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700723 def show_commands_at_teardown(self):
724 """ Allow subclass specific teardown logging additions."""
725 self.logger.info("--- No test specific show commands provided. ---")
726
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200728 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100729 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
730 (self.__class__.__name__, self._testMethodName,
731 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700732
733 try:
734 if not self.vpp_dead:
735 self.logger.debug(self.vapi.cli("show trace max 1000"))
736 self.logger.info(self.vapi.ppcli("show interface"))
737 self.logger.info(self.vapi.ppcli("show hardware"))
738 self.logger.info(self.statistics.set_errors_str())
739 self.logger.info(self.vapi.ppcli("show run"))
740 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400741 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700742 self.logger.info("Logging testcase specific show commands.")
743 self.show_commands_at_teardown()
744 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500745 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000746 m = self._testMethodName
747 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500748 tmp_api_trace = "/tmp/%s" % api_trace
749 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
750 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
751 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
752 vpp_api_trace_log))
753 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500754 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500755 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700756 except VppTransportShmemIOError:
757 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
758 "Cannot log show commands.")
759 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100760 else:
761 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200762
Damjan Marionf56b77a2016-10-03 19:44:57 +0200763 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200764 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800765 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200766 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100767 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400768
769 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
770 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100771 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100772 self.vpp_stdout_deque.append(
773 "--- test setUp() for %s.%s(%s) starts here ---\n" %
774 (self.__class__.__name__, self._testMethodName,
775 self._testMethodDoc))
776 self.vpp_stderr_deque.append(
777 "--- test setUp() for %s.%s(%s) starts here ---\n" %
778 (self.__class__.__name__, self._testMethodName,
779 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200780 self.vapi.cli("clear trace")
781 # store the test instance inside the test class - so that objects
782 # holding the class can access instance methods (like assertEqual)
783 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784
Damjan Marionf56b77a2016-10-03 19:44:57 +0200785 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200786 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200787 """
788 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200789
Klement Sekera75e7d132017-09-20 08:26:30 +0200790 :param interfaces: iterable interface indexes (if None,
791 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200792
Klement Sekeraf62ae122016-10-11 11:47:09 +0200793 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200794 if interfaces is None:
795 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200796 for i in interfaces:
797 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200798
Damjan Marionf56b77a2016-10-03 19:44:57 +0200799 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100800 def register_capture(cls, cap_name):
801 """ Register a capture in the testclass """
802 # add to the list of captures with current timestamp
803 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100804
805 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000806 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400807 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
808 # returns float("2.190522")
809 timestr = cls.vapi.cli('show clock')
810 head, sep, tail = timestr.partition(',')
811 head, sep, tail = head.partition('Time now')
812 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000813
814 @classmethod
815 def sleep_on_vpp_time(cls, sec):
816 """ Sleep according to time in VPP world """
817 # On a busy system with many processes
818 # we might end up with VPP time being slower than real world
819 # So take that into account when waiting for VPP to do something
820 start_time = cls.get_vpp_time()
821 while cls.get_vpp_time() - start_time < sec:
822 cls.sleep(0.1)
823
824 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100825 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000826 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100827 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200828 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000829 # PG, when starts, runs to completion -
830 # so let's avoid a race condition,
831 # and wait a little till it's done.
832 # Then clean it up - and then be gone.
833 deadline = time.time() + 300
834 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
835 cls.sleep(0.01) # yield
836 if time.time() > deadline:
837 cls.logger.error("Timeout waiting for pg to stop")
838 break
839 for stamp, cap_name in cls._captures:
840 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100841 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200842
Damjan Marionf56b77a2016-10-03 19:44:57 +0200843 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200844 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200845 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100846 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200847
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100848 :param interfaces: iterable indexes of the interfaces.
849 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200850
Klement Sekeraf62ae122016-10-11 11:47:09 +0200851 """
852 result = []
853 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200854 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200855 setattr(cls, intf.name, intf)
856 result.append(intf)
857 cls.pg_interfaces = result
858 return result
859
Matej Klotton0178d522016-11-04 11:11:44 +0100860 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200861 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100862 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100863 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100864
Klement Sekerab9ef2732018-06-24 22:49:33 +0200865 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100866 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100867 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200868 result = [VppLoInterface(cls) for i in range(count)]
869 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100870 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100871 cls.lo_interfaces = result
872 return result
873
Neale Ranns192b13f2019-03-15 02:16:20 -0700874 @classmethod
875 def create_bvi_interfaces(cls, count):
876 """
877 Create BVI interfaces.
878
879 :param count: number of interfaces created.
880 :returns: List of created interfaces.
881 """
882 result = [VppBviInterface(cls) for i in range(count)]
883 for intf in result:
884 setattr(cls, intf.name, intf)
885 cls.bvi_interfaces = result
886 return result
887
Damjan Marionf56b77a2016-10-03 19:44:57 +0200888 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200889 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200890 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200891 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200892 NOTE: Currently works only when Raw layer is present.
893
894 :param packet: packet
895 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200896 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200897
898 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200899 packet_len = len(packet) + 4
900 extend = size - packet_len
901 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200902 num = (extend // len(padding)) + 1
903 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200904
Klement Sekeradab231a2016-12-21 08:50:14 +0100905 @classmethod
906 def reset_packet_infos(cls):
907 """ Reset the list of packet info objects and packet counts to zero """
908 cls._packet_infos = {}
909 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200910
Klement Sekeradab231a2016-12-21 08:50:14 +0100911 @classmethod
912 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200913 """
914 Create packet info object containing the source and destination indexes
915 and add it to the testcase's packet info list
916
Klement Sekeradab231a2016-12-21 08:50:14 +0100917 :param VppInterface src_if: source interface
918 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200919
920 :returns: _PacketInfo object
921
922 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200923 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100924 info.index = len(cls._packet_infos)
925 info.src = src_if.sw_if_index
926 info.dst = dst_if.sw_if_index
927 if isinstance(dst_if, VppSubInterface):
928 dst_idx = dst_if.parent.sw_if_index
929 else:
930 dst_idx = dst_if.sw_if_index
931 if dst_idx in cls._packet_count_for_dst_if_idx:
932 cls._packet_count_for_dst_if_idx[dst_idx] += 1
933 else:
934 cls._packet_count_for_dst_if_idx[dst_idx] = 1
935 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200936 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200937
Damjan Marionf56b77a2016-10-03 19:44:57 +0200938 @staticmethod
939 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200940 """
941 Convert _PacketInfo object to packet payload
942
943 :param info: _PacketInfo object
944
945 :returns: string containing serialized data from packet info
946 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100947 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
948 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200949
Damjan Marionf56b77a2016-10-03 19:44:57 +0200950 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800951 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200952 """
953 Convert packet payload to _PacketInfo object
954
955 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700956 :type payload: <class 'scapy.packet.Raw'>
957 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800958 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700959 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200960 :returns: _PacketInfo object containing de-serialized data from payload
961
962 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800963 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200964 info = _PacketInfo()
965 info.index = int(numbers[0])
966 info.src = int(numbers[1])
967 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100968 info.ip = int(numbers[3])
969 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200970 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200971
Damjan Marionf56b77a2016-10-03 19:44:57 +0200972 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200973 """
974 Iterate over the packet info list stored in the testcase
975 Start iteration with first element if info is None
976 Continue based on index in info if info is specified
977
978 :param info: info or None
979 :returns: next info in list or None if no more infos
980 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200981 if info is None:
982 next_index = 0
983 else:
984 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100985 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200986 return None
987 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100988 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200989
Klement Sekeraf62ae122016-10-11 11:47:09 +0200990 def get_next_packet_info_for_interface(self, src_index, info):
991 """
992 Search the packet info list for the next packet info with same source
993 interface index
994
995 :param src_index: source interface index to search for
996 :param info: packet info - where to start the search
997 :returns: packet info or None
998
999 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001000 while True:
1001 info = self.get_next_packet_info(info)
1002 if info is None:
1003 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001004 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001005 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001006
Klement Sekeraf62ae122016-10-11 11:47:09 +02001007 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1008 """
1009 Search the packet info list for the next packet info with same source
1010 and destination interface indexes
1011
1012 :param src_index: source interface index to search for
1013 :param dst_index: destination interface index to search for
1014 :param info: packet info - where to start the search
1015 :returns: packet info or None
1016
1017 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001018 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001019 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001020 if info is None:
1021 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001022 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001023 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001024
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001025 def assert_equal(self, real_value, expected_value, name_or_class=None):
1026 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001027 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001028 return
1029 try:
1030 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1031 msg = msg % (getdoc(name_or_class).strip(),
1032 real_value, str(name_or_class(real_value)),
1033 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001034 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001035 msg = "Invalid %s: %s does not match expected value %s" % (
1036 name_or_class, real_value, expected_value)
1037
1038 self.assertEqual(real_value, expected_value, msg)
1039
Klement Sekerab17dd962017-01-09 07:43:48 +01001040 def assert_in_range(self,
1041 real_value,
1042 expected_min,
1043 expected_max,
1044 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001045 if name is None:
1046 msg = None
1047 else:
1048 msg = "Invalid %s: %s out of range <%s,%s>" % (
1049 name, real_value, expected_min, expected_max)
1050 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1051
Klement Sekerad81ae412018-05-16 10:52:54 +02001052 def assert_packet_checksums_valid(self, packet,
1053 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001054 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001055 udp_layers = ['UDP', 'UDPerror']
1056 checksum_fields = ['cksum', 'chksum']
1057 checksums = []
1058 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001059 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001060 while True:
1061 layer = temp.getlayer(counter)
1062 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001063 layer = layer.copy()
1064 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001065 for cf in checksum_fields:
1066 if hasattr(layer, cf):
1067 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001068 0 == getattr(layer, cf) and \
1069 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001070 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001071 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001072 checksums.append((counter, cf))
1073 else:
1074 break
1075 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001076 if 0 == len(checksums):
1077 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001078 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001079 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001080 calc_sum = getattr(temp[layer], cf)
1081 self.assert_equal(
1082 getattr(received[layer], cf), calc_sum,
1083 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1084 self.logger.debug(
1085 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1086 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001087
1088 def assert_checksum_valid(self, received_packet, layer,
1089 field_name='chksum',
1090 ignore_zero_checksum=False):
1091 """ Check checksum of received packet on given layer """
1092 received_packet_checksum = getattr(received_packet[layer], field_name)
1093 if ignore_zero_checksum and 0 == received_packet_checksum:
1094 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001095 recalculated = received_packet.__class__(
1096 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001097 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001098 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001099 self.assert_equal(received_packet_checksum,
1100 getattr(recalculated[layer], field_name),
1101 "packet checksum on layer: %s" % layer)
1102
1103 def assert_ip_checksum_valid(self, received_packet,
1104 ignore_zero_checksum=False):
1105 self.assert_checksum_valid(received_packet, 'IP',
1106 ignore_zero_checksum=ignore_zero_checksum)
1107
1108 def assert_tcp_checksum_valid(self, received_packet,
1109 ignore_zero_checksum=False):
1110 self.assert_checksum_valid(received_packet, 'TCP',
1111 ignore_zero_checksum=ignore_zero_checksum)
1112
1113 def assert_udp_checksum_valid(self, received_packet,
1114 ignore_zero_checksum=True):
1115 self.assert_checksum_valid(received_packet, 'UDP',
1116 ignore_zero_checksum=ignore_zero_checksum)
1117
1118 def assert_embedded_icmp_checksum_valid(self, received_packet):
1119 if received_packet.haslayer(IPerror):
1120 self.assert_checksum_valid(received_packet, 'IPerror')
1121 if received_packet.haslayer(TCPerror):
1122 self.assert_checksum_valid(received_packet, 'TCPerror')
1123 if received_packet.haslayer(UDPerror):
1124 self.assert_checksum_valid(received_packet, 'UDPerror',
1125 ignore_zero_checksum=True)
1126 if received_packet.haslayer(ICMPerror):
1127 self.assert_checksum_valid(received_packet, 'ICMPerror')
1128
1129 def assert_icmp_checksum_valid(self, received_packet):
1130 self.assert_checksum_valid(received_packet, 'ICMP')
1131 self.assert_embedded_icmp_checksum_valid(received_packet)
1132
1133 def assert_icmpv6_checksum_valid(self, pkt):
1134 if pkt.haslayer(ICMPv6DestUnreach):
1135 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1136 self.assert_embedded_icmp_checksum_valid(pkt)
1137 if pkt.haslayer(ICMPv6EchoRequest):
1138 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1139 if pkt.haslayer(ICMPv6EchoReply):
1140 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1141
Klement Sekera3a343d42019-05-16 14:35:46 +02001142 def get_packet_counter(self, counter):
1143 if counter.startswith("/"):
1144 counter_value = self.statistics.get_counter(counter)
1145 else:
1146 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001147 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001148 for i in range(1, len(counters) - 1):
1149 results = counters[i].split()
1150 if results[1] == counter:
1151 counter_value = int(results[0])
1152 break
1153 return counter_value
1154
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001155 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001156 counter_value = self.get_packet_counter(counter)
1157 self.assert_equal(counter_value, expected_value,
1158 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001159
Ole Troan233e4682019-05-16 15:01:34 +02001160 def assert_error_counter_equal(self, counter, expected_value):
1161 counter_value = self.statistics.get_err_counter(counter)
1162 self.assert_equal(counter_value, expected_value,
1163 "error counter `%s'" % counter)
1164
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001165 @classmethod
1166 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001167
1168 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1169 # * by Guido, only the main thread can be interrupted.
1170 # */
1171 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1172 if timeout == 0:
1173 # yield quantum
1174 if hasattr(os, 'sched_yield'):
1175 os.sched_yield()
1176 else:
1177 time.sleep(0)
1178 return
1179
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001180 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001181 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001182 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001183 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001184 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001185 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001186 "slept for %es instead of ~%es!",
1187 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001188
1189 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001190 "Finished sleep (%s) - slept %es (wanted %es)",
1191 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001192
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001193 def pg_send(self, intf, pkts, worker=None):
Neale Ranns52fae862018-01-08 04:41:42 -08001194 self.vapi.cli("clear trace")
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001195 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001196 self.pg_enable_capture(self.pg_interfaces)
1197 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001198
1199 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1200 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001201 if not timeout:
1202 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001203 for i in self.pg_interfaces:
1204 i.get_capture(0, timeout=timeout)
1205 i.assert_nothing_captured(remark=remark)
1206 timeout = 0.1
1207
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001208 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None):
Neale Rannsd7603d92019-03-28 08:56:10 +00001209 if not n_rx:
1210 n_rx = len(pkts)
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001211 self.pg_send(intf, pkts, worker=worker)
Neale Rannsd7603d92019-03-28 08:56:10 +00001212 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001213 return rx
1214
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001215 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1216 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001217 rx = output.get_capture(len(pkts))
1218 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001219 if not timeout:
1220 timeout = 1
1221 for i in self.pg_interfaces:
1222 if i not in outputs:
1223 i.get_capture(0, timeout=timeout)
1224 i.assert_nothing_captured()
1225 timeout = 0.1
1226
Neale Ranns52fae862018-01-08 04:41:42 -08001227 return rx
1228
Damjan Marionf56b77a2016-10-03 19:44:57 +02001229
juraj.linkes184870a2018-07-16 14:22:01 +02001230def get_testcase_doc_name(test):
1231 return getdoc(test.__class__).splitlines()[0]
1232
1233
Ole Trøan5ba91592018-11-22 10:01:09 +00001234def get_test_description(descriptions, test):
1235 short_description = test.shortDescription()
1236 if descriptions and short_description:
1237 return short_description
1238 else:
1239 return str(test)
1240
1241
juraj.linkes40dd73b2018-09-21 13:55:16 +02001242class TestCaseInfo(object):
1243 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1244 self.logger = logger
1245 self.tempdir = tempdir
1246 self.vpp_pid = vpp_pid
1247 self.vpp_bin_path = vpp_bin_path
1248 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001249
1250
Damjan Marionf56b77a2016-10-03 19:44:57 +02001251class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001252 """
1253 @property result_string
1254 String variable to store the test case result string.
1255 @property errors
1256 List variable containing 2-tuples of TestCase instances and strings
1257 holding formatted tracebacks. Each tuple represents a test which
1258 raised an unexpected exception.
1259 @property failures
1260 List variable containing 2-tuples of TestCase instances and strings
1261 holding formatted tracebacks. Each tuple represents a test where
1262 a failure was explicitly signalled using the TestCase.assert*()
1263 methods.
1264 """
1265
juraj.linkes40dd73b2018-09-21 13:55:16 +02001266 failed_test_cases_info = set()
1267 core_crash_test_cases_info = set()
1268 current_test_case_info = None
1269
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001270 def __init__(self, stream=None, descriptions=None, verbosity=None,
1271 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001272 """
Klement Sekerada505f62017-01-04 12:58:53 +01001273 :param stream File descriptor to store where to report test results.
1274 Set to the standard error stream by default.
1275 :param descriptions Boolean variable to store information if to use
1276 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001277 :param verbosity Integer variable to store required verbosity level.
1278 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001279 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001280 self.stream = stream
1281 self.descriptions = descriptions
1282 self.verbosity = verbosity
1283 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001284 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001285
Damjan Marionf56b77a2016-10-03 19:44:57 +02001286 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001287 """
1288 Record a test succeeded result
1289
1290 :param test:
1291
1292 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001293 if self.current_test_case_info:
1294 self.current_test_case_info.logger.debug(
1295 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1296 test._testMethodName,
1297 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001298 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001299 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001300
juraj.linkescae64f82018-09-19 15:01:47 +02001301 self.send_result_through_pipe(test, PASS)
1302
Klement Sekeraf62ae122016-10-11 11:47:09 +02001303 def addSkip(self, test, reason):
1304 """
1305 Record a test skipped.
1306
1307 :param test:
1308 :param reason:
1309
1310 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001311 if self.current_test_case_info:
1312 self.current_test_case_info.logger.debug(
1313 "--- addSkip() %s.%s(%s) called, reason is %s" %
1314 (test.__class__.__name__, test._testMethodName,
1315 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001316 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001317 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001318
juraj.linkescae64f82018-09-19 15:01:47 +02001319 self.send_result_through_pipe(test, SKIP)
1320
juraj.linkes40dd73b2018-09-21 13:55:16 +02001321 def symlink_failed(self):
1322 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001323 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001324 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001325 link_path = os.path.join(
1326 failed_dir,
1327 '%s-FAILED' %
1328 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001329
1330 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001331 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001332 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001333 "os.symlink(%s, %s)" %
1334 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001335 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001336 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001337 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001338 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001339 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001340
Klement Sekeraf413bef2017-08-15 07:09:02 +02001341 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001342 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001343
juraj.linkescae64f82018-09-19 15:01:47 +02001344 def send_result_through_pipe(self, test, result):
1345 if hasattr(self, 'test_framework_result_pipe'):
1346 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001347 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001348 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001349
juraj.linkes40dd73b2018-09-21 13:55:16 +02001350 def log_error(self, test, err, fn_name):
1351 if self.current_test_case_info:
1352 if isinstance(test, unittest.suite._ErrorHolder):
1353 test_name = test.description
1354 else:
1355 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1356 test._testMethodName,
1357 test._testMethodDoc)
1358 self.current_test_case_info.logger.debug(
1359 "--- %s() %s called, err is %s" %
1360 (fn_name, test_name, err))
1361 self.current_test_case_info.logger.debug(
1362 "formatted exception is:\n%s" %
1363 "".join(format_exception(*err)))
1364
1365 def add_error(self, test, err, unittest_fn, error_type):
1366 if error_type == FAIL:
1367 self.log_error(test, err, 'addFailure')
1368 error_type_str = colorize("FAIL", RED)
1369 elif error_type == ERROR:
1370 self.log_error(test, err, 'addError')
1371 error_type_str = colorize("ERROR", RED)
1372 else:
1373 raise Exception('Error type %s cannot be used to record an '
1374 'error or a failure' % error_type)
1375
1376 unittest_fn(self, test, err)
1377 if self.current_test_case_info:
1378 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1379 (error_type_str,
1380 self.current_test_case_info.tempdir)
1381 self.symlink_failed()
1382 self.failed_test_cases_info.add(self.current_test_case_info)
1383 if is_core_present(self.current_test_case_info.tempdir):
1384 if not self.current_test_case_info.core_crash_test:
1385 if isinstance(test, unittest.suite._ErrorHolder):
1386 test_name = str(test)
1387 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001388 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001389 get_testcase_doc_name(test), test.id())
1390 self.current_test_case_info.core_crash_test = test_name
1391 self.core_crash_test_cases_info.add(
1392 self.current_test_case_info)
1393 else:
1394 self.result_string = '%s [no temp dir]' % error_type_str
1395
1396 self.send_result_through_pipe(test, error_type)
1397
Damjan Marionf56b77a2016-10-03 19:44:57 +02001398 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001399 """
1400 Record a test failed result
1401
1402 :param test:
1403 :param err: error message
1404
1405 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001406 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001407
Damjan Marionf56b77a2016-10-03 19:44:57 +02001408 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001409 """
1410 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001411
Klement Sekeraf62ae122016-10-11 11:47:09 +02001412 :param test:
1413 :param err: error message
1414
1415 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001416 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001417
Damjan Marionf56b77a2016-10-03 19:44:57 +02001418 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001419 """
1420 Get test description
1421
1422 :param test:
1423 :returns: test description
1424
1425 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001426 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001427
Damjan Marionf56b77a2016-10-03 19:44:57 +02001428 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001429 """
1430 Start a test
1431
1432 :param test:
1433
1434 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001435
1436 def print_header(test):
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001437 test_doc = getdoc(test)
1438 if not test_doc:
1439 raise Exception("No doc string for test '%s'" % test.id())
1440 test_title = test_doc.splitlines()[0]
1441 test_title_colored = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001442 if test.is_tagged_run_solo():
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001443 # long live PEP-8 and 80 char width limitation...
1444 c = YELLOW
1445 test_title_colored = colorize("SOLO RUN: " + test_title, c)
1446
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001447 # This block may overwrite the colorized title above,
1448 # but we want this to stand out and be fixed
1449 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
1450 c = RED
1451 w = "FIXME with VPP workers: "
1452 test_title_colored = colorize(w + test_title, c)
1453
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001454 if not hasattr(test.__class__, '_header_printed'):
1455 print(double_line_delim)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001456 print(test_title_colored)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001457 print(double_line_delim)
1458 test.__class__._header_printed = True
1459
1460 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001461 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001462 unittest.TestResult.startTest(self, test)
1463 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001464 self.stream.writeln(
1465 "Starting " + self.getDescription(test) + " ...")
1466 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001467
Damjan Marionf56b77a2016-10-03 19:44:57 +02001468 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001469 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001470 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001471
1472 :param test:
1473
1474 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001475 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001476
Damjan Marionf56b77a2016-10-03 19:44:57 +02001477 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001478 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001479 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001480 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001481 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001482 else:
Ole Troan0c629322019-11-28 14:48:44 +01001483 self.stream.writeln("%-68s %4.2f %s" %
1484 (self.getDescription(test),
1485 time.time() - self.start_test,
1486 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001487
1488 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001489
Damjan Marionf56b77a2016-10-03 19:44:57 +02001490 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001491 """
1492 Print errors from running the test case
1493 """
juraj.linkesabec0122018-11-16 17:28:56 +01001494 if len(self.errors) > 0 or len(self.failures) > 0:
1495 self.stream.writeln()
1496 self.printErrorList('ERROR', self.errors)
1497 self.printErrorList('FAIL', self.failures)
1498
1499 # ^^ that is the last output from unittest before summary
1500 if not self.runner.print_summary:
1501 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1502 self.stream = devnull
1503 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001504
Damjan Marionf56b77a2016-10-03 19:44:57 +02001505 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001506 """
1507 Print error list to the output stream together with error type
1508 and test case description.
1509
1510 :param flavour: error type
1511 :param errors: iterable errors
1512
1513 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001514 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001515 self.stream.writeln(double_line_delim)
1516 self.stream.writeln("%s: %s" %
1517 (flavour, self.getDescription(test)))
1518 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001519 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001520
1521
Damjan Marionf56b77a2016-10-03 19:44:57 +02001522class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001523 """
Klement Sekera104543f2017-02-03 07:29:43 +01001524 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001525 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001526
Klement Sekeraf62ae122016-10-11 11:47:09 +02001527 @property
1528 def resultclass(self):
1529 """Class maintaining the results of the tests"""
1530 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001531
juraj.linkes184870a2018-07-16 14:22:01 +02001532 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001533 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001534 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001535 # ignore stream setting here, use hard-coded stdout to be in sync
1536 # with prints from VppTestCase methods ...
1537 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1538 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001539 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001540 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001541
juraj.linkesabec0122018-11-16 17:28:56 +01001542 self.orig_stream = self.stream
1543 self.resultclass.test_framework_result_pipe = result_pipe
1544
1545 self.print_summary = print_summary
1546
1547 def _makeResult(self):
1548 return self.resultclass(self.stream,
1549 self.descriptions,
1550 self.verbosity,
1551 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001552
Damjan Marionf56b77a2016-10-03 19:44:57 +02001553 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001554 """
1555 Run the tests
1556
1557 :param test:
1558
1559 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001560 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001561
1562 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001563 if not self.print_summary:
1564 self.stream = self.orig_stream
1565 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001566 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001567
1568
1569class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001570 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1571 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001572 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001573 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001574 if hasattr(self, 'testcase') and self.testcase.debug_all:
1575 if self.testcase.debug_gdbserver:
1576 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1577 .format(port=self.testcase.gdbserver_port)] + args
1578 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1579 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001580 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001581 self.app_name = os.path.basename(self.app_bin)
1582 if hasattr(self, 'role'):
1583 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001584 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001585 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001586 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001587 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001588
Dave Wallace24564332019-10-21 02:53:14 +00001589 def wait_for_enter(self):
1590 if not hasattr(self, 'testcase'):
1591 return
1592 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1593 print()
1594 print(double_line_delim)
1595 print("Spawned GDB Server for '{app}' with PID: {pid}"
1596 .format(app=self.app_name, pid=self.process.pid))
1597 elif self.testcase.debug_all and self.testcase.debug_gdb:
1598 print()
1599 print(double_line_delim)
1600 print("Spawned '{app}' with PID: {pid}"
1601 .format(app=self.app_name, pid=self.process.pid))
1602 else:
1603 return
1604 print(single_line_delim)
1605 print("You can debug '{app}' using:".format(app=self.app_name))
1606 if self.testcase.debug_gdbserver:
1607 print("sudo gdb " + self.app_bin +
1608 " -ex 'target remote localhost:{port}'"
1609 .format(port=self.testcase.gdbserver_port))
1610 print("Now is the time to attach gdb by running the above "
1611 "command, set up breakpoints etc., then resume from "
1612 "within gdb by issuing the 'continue' command")
1613 self.testcase.gdbserver_port += 1
1614 elif self.testcase.debug_gdb:
1615 print("sudo gdb " + self.app_bin +
1616 " -ex 'attach {pid}'".format(pid=self.process.pid))
1617 print("Now is the time to attach gdb by running the above "
1618 "command and set up breakpoints etc., then resume from"
1619 " within gdb by issuing the 'continue' command")
1620 print(single_line_delim)
1621 input("Press ENTER to continue running the testcase...")
1622
Neale Ranns812ed392017-10-16 04:20:13 -07001623 def run(self):
1624 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001625 if not os.path.exists(executable) or not os.access(
1626 executable, os.F_OK | os.X_OK):
1627 # Exit code that means some system file did not exist,
1628 # could not be opened, or had some other kind of error.
1629 self.result = os.EX_OSFILE
1630 raise EnvironmentError(
1631 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001632 self.logger.debug("Running executable: '{app}'"
1633 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001634 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001635 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001636 env["CK_LOG_FILE_NAME"] = "-"
1637 self.process = subprocess.Popen(
1638 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1639 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001640 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001641 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001642 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001643 self.logger.info("Return code is `%s'" % self.process.returncode)
1644 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001645 self.logger.info("Executable `{app}' wrote to stdout:"
1646 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001647 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001648 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001649 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001650 self.logger.info("Executable `{app}' wrote to stderr:"
1651 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001652 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001653 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001654 self.logger.info(single_line_delim)
1655 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001656
Klement Sekera6aa58b72019-05-16 14:34:55 +02001657
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001658if __name__ == '__main__':
1659 pass