blob: 4e0949bcf45b6d43c319515afce41126c74e2c0d [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
Ole Troan4376ab22021-03-03 10:40:05 +010036from vpp_papi.vpp_transport_socket import VppTransportSocketIOError
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:
Klement Sekera79a31db2021-03-12 18:16:10 +0100109 in_msg = ' while running %s.%s' % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400110
Klement Sekera79a31db2021-03-12 18:16:10 +0100111 if self.rv:
112 msg = "VPP subprocess died unexpectedly%s with return code: %d%s."\
113 % (in_msg, self.rv, ' [%s]' %
114 (self.signal_name if
115 self.signal_name is not None else ''))
116 else:
117 msg = "VPP subprocess died unexpectedly%s." % in_msg
118
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400119 super(VppDiedError, self).__init__(msg)
120
121
Damjan Marionf56b77a2016-10-03 19:44:57 +0200122class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200123 """Private class to create packet info object.
124
125 Help process information about the next packet.
126 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200127 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100128 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200129 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100130 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200131 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100132 #: Store the index of the destination packet generator interface
133 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200134 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100135 #: Store expected ip version
136 ip = -1
137 #: Store expected upper protocol
138 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100139 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200140 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200141
Matej Klotton16a14cd2016-12-07 15:09:13 +0100142 def __eq__(self, other):
143 index = self.index == other.index
144 src = self.src == other.src
145 dst = self.dst == other.dst
146 data = self.data == other.data
147 return index and src and dst and data
148
Klement Sekeraf62ae122016-10-11 11:47:09 +0200149
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100150def pump_output(testclass):
151 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100152 stdout_fragment = ""
153 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400154 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100155 readable = select.select([testclass.vpp.stdout.fileno(),
156 testclass.vpp.stderr.fileno(),
157 testclass.pump_thread_wakeup_pipe[0]],
158 [], [])[0]
159 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100160 read = os.read(testclass.vpp.stdout.fileno(), 102400)
161 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200162 split = read.decode('ascii',
163 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100164 if len(stdout_fragment) > 0:
165 split[0] = "%s%s" % (stdout_fragment, split[0])
166 if len(split) > 0 and split[-1].endswith("\n"):
167 limit = None
168 else:
169 limit = -1
170 stdout_fragment = split[-1]
171 testclass.vpp_stdout_deque.extend(split[:limit])
172 if not testclass.cache_vpp_output:
173 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100174 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100175 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100176 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100177 read = os.read(testclass.vpp.stderr.fileno(), 102400)
178 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200179 split = read.decode('ascii',
180 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100181 if len(stderr_fragment) > 0:
182 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200183 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100184 limit = None
185 else:
186 limit = -1
187 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200188
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100189 testclass.vpp_stderr_deque.extend(split[:limit])
190 if not testclass.cache_vpp_output:
191 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100192 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100193 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800194 # ignoring the dummy pipe here intentionally - the
195 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200196
197
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800198def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400199 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100200
Klement Sekera6aa58b72019-05-16 14:34:55 +0200201
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800202is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100203
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800204
205def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100206 return platform.machine() == 'aarch64'
207
Klement Sekera6aa58b72019-05-16 14:34:55 +0200208
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800209is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100210
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800211
212def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400213 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100214
Klement Sekera6aa58b72019-05-16 14:34:55 +0200215
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800216running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100217
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800218
Dave Barachd498c9e2020-03-10 16:59:39 -0400219def _running_gcov_tests():
220 return BoolEnvironmentVariable("GCOV_TESTS")
221
222
223running_gcov_tests = _running_gcov_tests()
224
225
Klement Sekera909a6a12017-08-08 04:33:53 +0200226class KeepAliveReporter(object):
227 """
228 Singleton object which reports test start to parent process
229 """
230 _shared_state = {}
231
232 def __init__(self):
233 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800234 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200235
236 @property
237 def pipe(self):
238 return self._pipe
239
240 @pipe.setter
241 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800242 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200243 raise Exception("Internal error - pipe should only be set once.")
244 self._pipe = pipe
245
juraj.linkes40dd73b2018-09-21 13:55:16 +0200246 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200247 """
248 Write current test tmpdir & desc to keep-alive pipe to signal liveness
249 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200250 if self.pipe is None:
251 # if not running forked..
252 return
253
Klement Sekera909a6a12017-08-08 04:33:53 +0200254 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200255 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200256 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200257 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200258
Dave Wallacee2efd122017-09-30 22:04:21 -0400259 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200260
261
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000262class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000263 # marks the suites that must run at the end
264 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000265 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000266 # marks the suites broken on VPP multi-worker
267 FIXME_VPP_WORKERS = 2
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000268
269
270def create_tag_decorator(e):
271 def decorator(cls):
272 try:
273 cls.test_tags.append(e)
274 except AttributeError:
275 cls.test_tags = [e]
276 return cls
277 return decorator
278
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000279
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000280tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000281tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000282
283
Damjan Marionf56b77a2016-10-03 19:44:57 +0200284class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100285 """This subclass is a base class for VPP test cases that are implemented as
286 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200287 """
288
Ole Troana45dc072018-12-21 16:04:22 +0100289 extra_vpp_punt_config = []
290 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500291 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400292 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100293
Klement Sekeraf62ae122016-10-11 11:47:09 +0200294 @property
295 def packet_infos(self):
296 """List of packet infos"""
297 return self._packet_infos
298
Klement Sekeradab231a2016-12-21 08:50:14 +0100299 @classmethod
300 def get_packet_count_for_if_idx(cls, dst_if_index):
301 """Get the number of packet info for specified destination if index"""
302 if dst_if_index in cls._packet_count_for_dst_if_idx:
303 return cls._packet_count_for_dst_if_idx[dst_if_index]
304 else:
305 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200306
307 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000308 def has_tag(cls, tag):
309 """ if the test case has a given tag - return true """
310 try:
311 return tag in cls.test_tags
312 except AttributeError:
313 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000314 return False
315
316 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000317 def is_tagged_run_solo(cls):
318 """ if the test case class is timing-sensitive - return true """
319 return cls.has_tag(TestCaseTag.RUN_SOLO)
320
321 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200322 def instance(cls):
323 """Return the instance of this testcase"""
324 return cls.test_instance
325
Damjan Marionf56b77a2016-10-03 19:44:57 +0200326 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200327 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000328 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200329 cls.debug_core = False
330 cls.debug_gdb = False
331 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000332 cls.debug_all = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200333 if d is None:
334 return
335 dl = d.lower()
336 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200337 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000338 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200339 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000340 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200341 cls.debug_gdbserver = True
342 else:
343 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000344 if dl == "gdb-all" or dl == "gdbserver-all":
345 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200346
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800347 @staticmethod
348 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200349 cpu_usage_list = [set(range(psutil.cpu_count()))]
350 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
351 if 'vpp_main' == p.info['name']]
352 for vpp_process in vpp_processes:
353 for cpu_usage_set in cpu_usage_list:
354 try:
355 cpu_num = vpp_process.cpu_num()
356 if cpu_num in cpu_usage_set:
357 cpu_usage_set_index = cpu_usage_list.index(
358 cpu_usage_set)
359 if cpu_usage_set_index == len(cpu_usage_list) - 1:
360 cpu_usage_list.append({cpu_num})
361 else:
362 cpu_usage_list[cpu_usage_set_index + 1].add(
363 cpu_num)
364 cpu_usage_set.remove(cpu_num)
365 break
366 except psutil.NoSuchProcess:
367 pass
368
369 for cpu_usage_set in cpu_usage_list:
370 if len(cpu_usage_set) > 0:
371 min_usage_set = cpu_usage_set
372 break
373
374 return random.choice(tuple(min_usage_set))
375
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800376 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200377 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200378 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400379 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100380 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400381 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100382 c = os.getenv("CACHE_OUTPUT", "1")
383 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200384 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100385 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
386 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400387 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100388 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
389 plugin_path = None
390 if cls.plugin_path is not None:
391 if cls.extern_plugin_path is not None:
392 plugin_path = "%s:%s" % (
393 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100394 else:
395 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100396 elif cls.extern_plugin_path is not None:
397 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100398 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100399 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100400 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100401 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100402 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100403 if size is not None:
404 coredump_size = "coredump-size %s" % size
405 if coredump_size is None:
406 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200407
Ole Troana45dc072018-12-21 16:04:22 +0100408 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000409 if not hasattr(cls, "worker_config"):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000410 cls.worker_config = os.getenv("VPP_WORKER_CONFIG", "")
411 if cls.worker_config != "":
412 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
413 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200414
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000415 default_variant = os.getenv("VARIANT")
416 if default_variant is not None:
417 default_variant = "defaults { %s 100 }" % default_variant
418 else:
419 default_variant = ""
420
Dave Barach77841402020-04-29 17:04:10 -0400421 api_fuzzing = os.getenv("API_FUZZ")
422 if api_fuzzing is None:
423 api_fuzzing = 'off'
424
Ole Troana45dc072018-12-21 16:04:22 +0100425 cls.vpp_cmdline = [cls.vpp_bin, "unix",
426 "{", "nodaemon", debug_cli, "full-coredump",
427 coredump_size, "runtime-dir", cls.tempdir, "}",
428 "api-trace", "{", "on", "}", "api-segment", "{",
429 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000430 "main-core", str(cpu_core_number),
431 cls.worker_config, "}",
Dave Barach4ed25982019-12-25 09:24:58 -0500432 "physmem", "{", "max-size", "32m", "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200433 "statseg", "{", "socket-name", cls.stats_sock, "}",
434 "socksvr", "{", "socket-name", cls.api_sock, "}",
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000435 "node { ", default_variant, "}",
Dave Barach77841402020-04-29 17:04:10 -0400436 "api-fuzz {", api_fuzzing, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200437 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100438 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200439 "}", "plugin", "rdma_plugin.so", "{", "disable",
Neale Ranns2b202bc2020-09-21 08:17:51 +0000440 "}", "plugin", "lisp_unittest_plugin.so", "{",
441 "enable",
Ole Troana45dc072018-12-21 16:04:22 +0100442 "}", "plugin", "unittest_plugin.so", "{", "enable",
443 "}"] + cls.extra_vpp_plugin_config + ["}", ]
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000444
Ole Troana45dc072018-12-21 16:04:22 +0100445 if cls.extra_vpp_punt_config is not None:
446 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100447 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100448 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400449 if cls.test_plugin_path is not None:
450 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
451
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100452 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
453 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200454
455 @classmethod
456 def wait_for_enter(cls):
457 if cls.debug_gdbserver:
458 print(double_line_delim)
459 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
460 elif cls.debug_gdb:
461 print(double_line_delim)
462 print("Spawned VPP with PID: %d" % cls.vpp.pid)
463 else:
464 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
465 return
466 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000467 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200468 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400469 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000470 " -ex 'target remote localhost:{port}'"
471 .format(port=cls.gdbserver_port))
472 print("Now is the time to attach gdb by running the above "
473 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200474 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000475 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200476 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400477 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000478 print("Now is the time to attach gdb by running the above "
479 "command and set up breakpoints etc., then resume VPP from"
480 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200481 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800482 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200483
484 @classmethod
485 def run_vpp(cls):
486 cmdline = cls.vpp_cmdline
487
488 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100489 gdbserver = '/usr/bin/gdbserver'
490 if not os.path.isfile(gdbserver) or \
491 not os.access(gdbserver, os.X_OK):
492 raise Exception("gdbserver binary '%s' does not exist or is "
493 "not executable" % gdbserver)
494
Dave Wallace24564332019-10-21 02:53:14 +0000495 cmdline = [gdbserver, 'localhost:{port}'
496 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200497 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
498
Klement Sekera931be3a2016-11-03 05:36:01 +0100499 try:
500 cls.vpp = subprocess.Popen(cmdline,
501 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100502 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800503 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800504 cls.logger.critical("Subprocess returned with non-0 return code: ("
505 "%s)", e.returncode)
506 raise
507 except OSError as e:
508 cls.logger.critical("Subprocess returned with OS error: "
509 "(%s) %s", e.errno, e.strerror)
510 raise
511 except Exception as e:
512 cls.logger.exception("Subprocess returned unexpected from "
513 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100514 raise
515
Klement Sekera277b89c2016-10-28 13:20:27 +0200516 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100517
Damjan Marionf56b77a2016-10-03 19:44:57 +0200518 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000519 def wait_for_coredump(cls):
520 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400521 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000522 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400523 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000524 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400525 ok = False
526 while time.time() < deadline:
527 cls.sleep(1)
528 size = curr_size
529 curr_size = os.path.getsize(corefile)
530 if size == curr_size:
531 ok = True
532 break
533 if not ok:
534 cls.logger.error("Timed out waiting for coredump to complete:"
535 " %s", corefile)
536 else:
537 cls.logger.error("Coredump complete: %s, size %d",
538 corefile, curr_size)
539
540 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200541 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200542 """
543 Perform class setup before running the testcase
544 Remove shared memory files, start vpp and connect the vpp-api
545 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800546 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100547 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100548 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000549 seed = os.environ["RND_SEED"]
550 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100551 if hasattr(cls, 'parallel_handler'):
552 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100553 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700554
Klement Sekeraf62ae122016-10-11 11:47:09 +0200555 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200556 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200557 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200558 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200559 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
560 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100561 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
562 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200563 cls.file_handler.setLevel(DEBUG)
564 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700565 cls.logger.debug("--- setUpClass() for %s called ---" %
566 cls.__name__)
Ole Troan4376ab22021-03-03 10:40:05 +0100567 cls.shm_prefix = os.path.basename(cls.tempdir) # Only used for VAPI
Klement Sekeraf62ae122016-10-11 11:47:09 +0200568 os.chdir(cls.tempdir)
Ole Troan4376ab22021-03-03 10:40:05 +0100569 cls.logger.info("Temporary dir is %s, api socket is %s",
570 cls.tempdir, cls.api_sock)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000571 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200572 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100573 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100574 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200575 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100576 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100577 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200578 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200579 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200580 # need to catch exceptions here because if we raise, then the cleanup
581 # doesn't get called and we might end with a zombie vpp
582 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200583 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200584 cls.reporter.send_keep_alive(cls, 'setUpClass')
585 VppTestResult.current_test_case_info = TestCaseInfo(
586 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100587 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100588 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100589 cls.pump_thread_stop_flag = Event()
590 cls.pump_thread_wakeup_pipe = os.pipe()
591 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100592 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100593 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200594 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400595 cls.vapi_response_timeout = 0
Ole Troan4376ab22021-03-03 10:40:05 +0100596 cls.vapi = VppPapiProvider(cls.__name__, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400597 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100598 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400599 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100600 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400601 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100602 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200603 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200604 try:
605 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100606 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200607 cls.vpp_startup_failed = True
608 cls.logger.critical(
609 "VPP died shortly after startup, check the"
610 " output to standard error for possible cause")
611 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100612 try:
613 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100614 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500615 cls.logger.debug("Exception connecting to vapi: %s" % e)
616 cls.vapi.disconnect()
617
Klement Sekera085f5c02016-11-24 01:59:16 +0100618 if cls.debug_gdbserver:
619 print(colorize("You're running VPP inside gdbserver but "
620 "VPP-API connection failed, did you forget "
621 "to 'continue' VPP from within gdb?", RED))
Ole Troan4376ab22021-03-03 10:40:05 +0100622 raise e
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400623 except vpp_papi.VPPRuntimeError as e:
624 cls.logger.debug("%s" % e)
625 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100626 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000627 except Exception as e:
628 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400629 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100630 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200631
Damjan Marionf56b77a2016-10-03 19:44:57 +0200632 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500633 def _debug_quit(cls):
634 if (cls.debug_gdbserver or cls.debug_gdb):
635 try:
636 cls.vpp.poll()
637
638 if cls.vpp.returncode is None:
639 print()
640 print(double_line_delim)
641 print("VPP or GDB server is still running")
642 print(single_line_delim)
643 input("When done debugging, press ENTER to kill the "
644 "process and finish running the testcase...")
645 except AttributeError:
646 pass
647
648 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200649 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200650 """
651 Disconnect vpp-api, kill vpp and cleanup shared memory files
652 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500653 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200654
juraj.linkes184870a2018-07-16 14:22:01 +0200655 # first signal that we want to stop the pump thread, then wake it up
656 if hasattr(cls, 'pump_thread_stop_flag'):
657 cls.pump_thread_stop_flag.set()
658 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100659 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100660 if hasattr(cls, 'pump_thread'):
661 cls.logger.debug("Waiting for pump thread to stop")
662 cls.pump_thread.join()
663 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500664 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100665 cls.vpp_stderr_reader_thread.join()
666
Klement Sekeraf62ae122016-10-11 11:47:09 +0200667 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100668 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100669 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700670 cls.logger.debug("Disconnecting class vapi client on %s",
671 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100672 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700673 cls.logger.debug("Deleting class vapi attribute on %s",
674 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100675 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200676 cls.vpp.poll()
677 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000678 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100679 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400680 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100681 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100682 try:
683 outs, errs = cls.vpp.communicate(timeout=5)
684 except subprocess.TimeoutExpired:
685 cls.vpp.kill()
686 outs, errs = cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700687 cls.logger.debug("Deleting class vpp attribute on %s",
688 cls.__name__)
Paul Vinciguerrad1f05f72020-12-16 21:03:16 -0500689 cls.vpp.stdout.close()
690 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200691 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200692
Klement Sekera3747c752017-04-10 06:30:17 +0200693 if cls.vpp_startup_failed:
694 stdout_log = cls.logger.info
695 stderr_log = cls.logger.critical
696 else:
697 stdout_log = cls.logger.info
698 stderr_log = cls.logger.info
699
Klement Sekerae4504c62016-12-08 10:16:41 +0100700 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200701 stdout_log(single_line_delim)
702 stdout_log('VPP output to stdout while running %s:', cls.__name__)
703 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100704 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200705 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
706 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200707 stdout_log('\n%s', vpp_output)
708 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200709
Klement Sekerae4504c62016-12-08 10:16:41 +0100710 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200711 stderr_log(single_line_delim)
712 stderr_log('VPP output to stderr while running %s:', cls.__name__)
713 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100714 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200715 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
716 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200717 stderr_log('\n%s', vpp_output)
718 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200719
Damjan Marionf56b77a2016-10-03 19:44:57 +0200720 @classmethod
721 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200722 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700723 cls.logger.debug("--- tearDownClass() for %s called ---" %
724 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200725 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200726 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200727 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100728 cls.reset_packet_infos()
729 if debug_framework:
730 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200731
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700732 def show_commands_at_teardown(self):
733 """ Allow subclass specific teardown logging additions."""
734 self.logger.info("--- No test specific show commands provided. ---")
735
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200737 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100738 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
739 (self.__class__.__name__, self._testMethodName,
740 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700741
742 try:
743 if not self.vpp_dead:
744 self.logger.debug(self.vapi.cli("show trace max 1000"))
745 self.logger.info(self.vapi.ppcli("show interface"))
746 self.logger.info(self.vapi.ppcli("show hardware"))
747 self.logger.info(self.statistics.set_errors_str())
748 self.logger.info(self.vapi.ppcli("show run"))
749 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400750 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700751 self.logger.info("Logging testcase specific show commands.")
752 self.show_commands_at_teardown()
753 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500754 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000755 m = self._testMethodName
756 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500757 tmp_api_trace = "/tmp/%s" % api_trace
758 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
759 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
760 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
761 vpp_api_trace_log))
762 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500763 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500764 vpp_api_trace_log))
Ole Troan4376ab22021-03-03 10:40:05 +0100765 except VppTransportSocketIOError:
766 self.logger.debug("VppTransportSocketIOError: Vpp dead. "
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700767 "Cannot log show commands.")
768 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100769 else:
770 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200771
Damjan Marionf56b77a2016-10-03 19:44:57 +0200772 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200773 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800774 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200775 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100776 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400777
778 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
779 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100780 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100781 self.vpp_stdout_deque.append(
782 "--- test setUp() for %s.%s(%s) starts here ---\n" %
783 (self.__class__.__name__, self._testMethodName,
784 self._testMethodDoc))
785 self.vpp_stderr_deque.append(
786 "--- test setUp() for %s.%s(%s) starts here ---\n" %
787 (self.__class__.__name__, self._testMethodName,
788 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200789 self.vapi.cli("clear trace")
790 # store the test instance inside the test class - so that objects
791 # holding the class can access instance methods (like assertEqual)
792 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200793
Damjan Marionf56b77a2016-10-03 19:44:57 +0200794 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200795 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200796 """
797 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200798
Klement Sekera75e7d132017-09-20 08:26:30 +0200799 :param interfaces: iterable interface indexes (if None,
800 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200801
Klement Sekeraf62ae122016-10-11 11:47:09 +0200802 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200803 if interfaces is None:
804 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200805 for i in interfaces:
806 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200807
Damjan Marionf56b77a2016-10-03 19:44:57 +0200808 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100809 def register_capture(cls, cap_name):
810 """ Register a capture in the testclass """
811 # add to the list of captures with current timestamp
812 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100813
814 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000815 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400816 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
817 # returns float("2.190522")
818 timestr = cls.vapi.cli('show clock')
819 head, sep, tail = timestr.partition(',')
820 head, sep, tail = head.partition('Time now')
821 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000822
823 @classmethod
824 def sleep_on_vpp_time(cls, sec):
825 """ Sleep according to time in VPP world """
826 # On a busy system with many processes
827 # we might end up with VPP time being slower than real world
828 # So take that into account when waiting for VPP to do something
829 start_time = cls.get_vpp_time()
830 while cls.get_vpp_time() - start_time < sec:
831 cls.sleep(0.1)
832
833 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100834 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000835 """ Enable the PG, wait till it is done, then clean up """
Benoît Ganne8c45e512021-02-19 16:39:13 +0100836 if trace:
837 cls.vapi.cli("clear trace")
838 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200839 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000840 # PG, when starts, runs to completion -
841 # so let's avoid a race condition,
842 # and wait a little till it's done.
843 # Then clean it up - and then be gone.
844 deadline = time.time() + 300
845 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
846 cls.sleep(0.01) # yield
847 if time.time() > deadline:
848 cls.logger.error("Timeout waiting for pg to stop")
849 break
850 for stamp, cap_name in cls._captures:
851 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100852 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200853
Damjan Marionf56b77a2016-10-03 19:44:57 +0200854 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200855 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200856 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100857 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200858
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100859 :param interfaces: iterable indexes of the interfaces.
860 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200861
Klement Sekeraf62ae122016-10-11 11:47:09 +0200862 """
863 result = []
864 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200865 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200866 setattr(cls, intf.name, intf)
867 result.append(intf)
868 cls.pg_interfaces = result
869 return result
870
Matej Klotton0178d522016-11-04 11:11:44 +0100871 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200872 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100873 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100874 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100875
Klement Sekerab9ef2732018-06-24 22:49:33 +0200876 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100877 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100878 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200879 result = [VppLoInterface(cls) for i in range(count)]
880 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100881 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100882 cls.lo_interfaces = result
883 return result
884
Neale Ranns192b13f2019-03-15 02:16:20 -0700885 @classmethod
886 def create_bvi_interfaces(cls, count):
887 """
888 Create BVI interfaces.
889
890 :param count: number of interfaces created.
891 :returns: List of created interfaces.
892 """
893 result = [VppBviInterface(cls) for i in range(count)]
894 for intf in result:
895 setattr(cls, intf.name, intf)
896 cls.bvi_interfaces = result
897 return result
898
Damjan Marionf56b77a2016-10-03 19:44:57 +0200899 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200900 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200901 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200902 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200903 NOTE: Currently works only when Raw layer is present.
904
905 :param packet: packet
906 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200907 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200908
909 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200910 packet_len = len(packet) + 4
911 extend = size - packet_len
912 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200913 num = (extend // len(padding)) + 1
914 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200915
Klement Sekeradab231a2016-12-21 08:50:14 +0100916 @classmethod
917 def reset_packet_infos(cls):
918 """ Reset the list of packet info objects and packet counts to zero """
919 cls._packet_infos = {}
920 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200921
Klement Sekeradab231a2016-12-21 08:50:14 +0100922 @classmethod
923 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200924 """
925 Create packet info object containing the source and destination indexes
926 and add it to the testcase's packet info list
927
Klement Sekeradab231a2016-12-21 08:50:14 +0100928 :param VppInterface src_if: source interface
929 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200930
931 :returns: _PacketInfo object
932
933 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200934 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100935 info.index = len(cls._packet_infos)
936 info.src = src_if.sw_if_index
937 info.dst = dst_if.sw_if_index
938 if isinstance(dst_if, VppSubInterface):
939 dst_idx = dst_if.parent.sw_if_index
940 else:
941 dst_idx = dst_if.sw_if_index
942 if dst_idx in cls._packet_count_for_dst_if_idx:
943 cls._packet_count_for_dst_if_idx[dst_idx] += 1
944 else:
945 cls._packet_count_for_dst_if_idx[dst_idx] = 1
946 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200947 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200948
Damjan Marionf56b77a2016-10-03 19:44:57 +0200949 @staticmethod
950 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200951 """
952 Convert _PacketInfo object to packet payload
953
954 :param info: _PacketInfo object
955
956 :returns: string containing serialized data from packet info
957 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100958 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
959 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200960
Damjan Marionf56b77a2016-10-03 19:44:57 +0200961 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800962 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200963 """
964 Convert packet payload to _PacketInfo object
965
966 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700967 :type payload: <class 'scapy.packet.Raw'>
968 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800969 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700970 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200971 :returns: _PacketInfo object containing de-serialized data from payload
972
973 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800974 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200975 info = _PacketInfo()
976 info.index = int(numbers[0])
977 info.src = int(numbers[1])
978 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100979 info.ip = int(numbers[3])
980 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200981 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200982
Damjan Marionf56b77a2016-10-03 19:44:57 +0200983 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200984 """
985 Iterate over the packet info list stored in the testcase
986 Start iteration with first element if info is None
987 Continue based on index in info if info is specified
988
989 :param info: info or None
990 :returns: next info in list or None if no more infos
991 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200992 if info is None:
993 next_index = 0
994 else:
995 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100996 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200997 return None
998 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100999 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001000
Klement Sekeraf62ae122016-10-11 11:47:09 +02001001 def get_next_packet_info_for_interface(self, src_index, info):
1002 """
1003 Search the packet info list for the next packet info with same source
1004 interface index
1005
1006 :param src_index: source interface index to search for
1007 :param info: packet info - where to start the search
1008 :returns: packet info or None
1009
1010 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001011 while True:
1012 info = self.get_next_packet_info(info)
1013 if info is None:
1014 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001015 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001016 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001017
Klement Sekeraf62ae122016-10-11 11:47:09 +02001018 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1019 """
1020 Search the packet info list for the next packet info with same source
1021 and destination interface indexes
1022
1023 :param src_index: source interface index to search for
1024 :param dst_index: destination interface index to search for
1025 :param info: packet info - where to start the search
1026 :returns: packet info or None
1027
1028 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001029 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001030 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001031 if info is None:
1032 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001033 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001034 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001035
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001036 def assert_equal(self, real_value, expected_value, name_or_class=None):
1037 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001038 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001039 return
1040 try:
1041 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1042 msg = msg % (getdoc(name_or_class).strip(),
1043 real_value, str(name_or_class(real_value)),
1044 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001045 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001046 msg = "Invalid %s: %s does not match expected value %s" % (
1047 name_or_class, real_value, expected_value)
1048
1049 self.assertEqual(real_value, expected_value, msg)
1050
Klement Sekerab17dd962017-01-09 07:43:48 +01001051 def assert_in_range(self,
1052 real_value,
1053 expected_min,
1054 expected_max,
1055 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001056 if name is None:
1057 msg = None
1058 else:
1059 msg = "Invalid %s: %s out of range <%s,%s>" % (
1060 name, real_value, expected_min, expected_max)
1061 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1062
Klement Sekerad81ae412018-05-16 10:52:54 +02001063 def assert_packet_checksums_valid(self, packet,
1064 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001065 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001066 udp_layers = ['UDP', 'UDPerror']
1067 checksum_fields = ['cksum', 'chksum']
1068 checksums = []
1069 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001070 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001071 while True:
1072 layer = temp.getlayer(counter)
1073 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001074 layer = layer.copy()
1075 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001076 for cf in checksum_fields:
1077 if hasattr(layer, cf):
1078 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001079 0 == getattr(layer, cf) and \
1080 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001081 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001082 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001083 checksums.append((counter, cf))
1084 else:
1085 break
1086 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001087 if 0 == len(checksums):
1088 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001089 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001090 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001091 calc_sum = getattr(temp[layer], cf)
1092 self.assert_equal(
1093 getattr(received[layer], cf), calc_sum,
1094 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1095 self.logger.debug(
1096 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1097 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001098
1099 def assert_checksum_valid(self, received_packet, layer,
1100 field_name='chksum',
1101 ignore_zero_checksum=False):
1102 """ Check checksum of received packet on given layer """
1103 received_packet_checksum = getattr(received_packet[layer], field_name)
1104 if ignore_zero_checksum and 0 == received_packet_checksum:
1105 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001106 recalculated = received_packet.__class__(
1107 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001108 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001109 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001110 self.assert_equal(received_packet_checksum,
1111 getattr(recalculated[layer], field_name),
1112 "packet checksum on layer: %s" % layer)
1113
1114 def assert_ip_checksum_valid(self, received_packet,
1115 ignore_zero_checksum=False):
1116 self.assert_checksum_valid(received_packet, 'IP',
1117 ignore_zero_checksum=ignore_zero_checksum)
1118
1119 def assert_tcp_checksum_valid(self, received_packet,
1120 ignore_zero_checksum=False):
1121 self.assert_checksum_valid(received_packet, 'TCP',
1122 ignore_zero_checksum=ignore_zero_checksum)
1123
1124 def assert_udp_checksum_valid(self, received_packet,
1125 ignore_zero_checksum=True):
1126 self.assert_checksum_valid(received_packet, 'UDP',
1127 ignore_zero_checksum=ignore_zero_checksum)
1128
1129 def assert_embedded_icmp_checksum_valid(self, received_packet):
1130 if received_packet.haslayer(IPerror):
1131 self.assert_checksum_valid(received_packet, 'IPerror')
1132 if received_packet.haslayer(TCPerror):
1133 self.assert_checksum_valid(received_packet, 'TCPerror')
1134 if received_packet.haslayer(UDPerror):
1135 self.assert_checksum_valid(received_packet, 'UDPerror',
1136 ignore_zero_checksum=True)
1137 if received_packet.haslayer(ICMPerror):
1138 self.assert_checksum_valid(received_packet, 'ICMPerror')
1139
1140 def assert_icmp_checksum_valid(self, received_packet):
1141 self.assert_checksum_valid(received_packet, 'ICMP')
1142 self.assert_embedded_icmp_checksum_valid(received_packet)
1143
1144 def assert_icmpv6_checksum_valid(self, pkt):
1145 if pkt.haslayer(ICMPv6DestUnreach):
1146 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1147 self.assert_embedded_icmp_checksum_valid(pkt)
1148 if pkt.haslayer(ICMPv6EchoRequest):
1149 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1150 if pkt.haslayer(ICMPv6EchoReply):
1151 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1152
Klement Sekera3a343d42019-05-16 14:35:46 +02001153 def get_packet_counter(self, counter):
1154 if counter.startswith("/"):
1155 counter_value = self.statistics.get_counter(counter)
1156 else:
1157 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001158 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001159 for i in range(1, len(counters) - 1):
1160 results = counters[i].split()
1161 if results[1] == counter:
1162 counter_value = int(results[0])
1163 break
1164 return counter_value
1165
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001166 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001167 counter_value = self.get_packet_counter(counter)
1168 self.assert_equal(counter_value, expected_value,
1169 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001170
Ole Troan233e4682019-05-16 15:01:34 +02001171 def assert_error_counter_equal(self, counter, expected_value):
1172 counter_value = self.statistics.get_err_counter(counter)
1173 self.assert_equal(counter_value, expected_value,
1174 "error counter `%s'" % counter)
1175
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001176 @classmethod
1177 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001178
1179 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1180 # * by Guido, only the main thread can be interrupted.
1181 # */
1182 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1183 if timeout == 0:
1184 # yield quantum
1185 if hasattr(os, 'sched_yield'):
1186 os.sched_yield()
1187 else:
1188 time.sleep(0)
1189 return
1190
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001191 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001192 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001193 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001194 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001195 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001196 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001197 "slept for %es instead of ~%es!",
1198 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001199
1200 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001201 "Finished sleep (%s) - slept %es (wanted %es)",
1202 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001203
Benoît Ganne8c45e512021-02-19 16:39:13 +01001204 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001205 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001206 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001207 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001208
1209 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1210 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001211 if not timeout:
1212 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001213 for i in self.pg_interfaces:
1214 i.get_capture(0, timeout=timeout)
1215 i.assert_nothing_captured(remark=remark)
1216 timeout = 0.1
1217
Benoît Ganne8c45e512021-02-19 16:39:13 +01001218 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1219 trace=True):
Neale Rannsd7603d92019-03-28 08:56:10 +00001220 if not n_rx:
1221 n_rx = len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001222 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001223 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001224 return rx
1225
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001226 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1227 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001228 rx = output.get_capture(len(pkts))
1229 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001230 if not timeout:
1231 timeout = 1
1232 for i in self.pg_interfaces:
1233 if i not in outputs:
1234 i.get_capture(0, timeout=timeout)
1235 i.assert_nothing_captured()
1236 timeout = 0.1
1237
Neale Ranns52fae862018-01-08 04:41:42 -08001238 return rx
1239
Damjan Marionf56b77a2016-10-03 19:44:57 +02001240
juraj.linkes184870a2018-07-16 14:22:01 +02001241def get_testcase_doc_name(test):
1242 return getdoc(test.__class__).splitlines()[0]
1243
1244
Ole Trøan5ba91592018-11-22 10:01:09 +00001245def get_test_description(descriptions, test):
1246 short_description = test.shortDescription()
1247 if descriptions and short_description:
1248 return short_description
1249 else:
1250 return str(test)
1251
1252
juraj.linkes40dd73b2018-09-21 13:55:16 +02001253class TestCaseInfo(object):
1254 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1255 self.logger = logger
1256 self.tempdir = tempdir
1257 self.vpp_pid = vpp_pid
1258 self.vpp_bin_path = vpp_bin_path
1259 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001260
1261
Damjan Marionf56b77a2016-10-03 19:44:57 +02001262class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001263 """
1264 @property result_string
1265 String variable to store the test case result string.
1266 @property errors
1267 List variable containing 2-tuples of TestCase instances and strings
1268 holding formatted tracebacks. Each tuple represents a test which
1269 raised an unexpected exception.
1270 @property failures
1271 List variable containing 2-tuples of TestCase instances and strings
1272 holding formatted tracebacks. Each tuple represents a test where
1273 a failure was explicitly signalled using the TestCase.assert*()
1274 methods.
1275 """
1276
juraj.linkes40dd73b2018-09-21 13:55:16 +02001277 failed_test_cases_info = set()
1278 core_crash_test_cases_info = set()
1279 current_test_case_info = None
1280
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001281 def __init__(self, stream=None, descriptions=None, verbosity=None,
1282 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001283 """
Klement Sekerada505f62017-01-04 12:58:53 +01001284 :param stream File descriptor to store where to report test results.
1285 Set to the standard error stream by default.
1286 :param descriptions Boolean variable to store information if to use
1287 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001288 :param verbosity Integer variable to store required verbosity level.
1289 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001290 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001291 self.stream = stream
1292 self.descriptions = descriptions
1293 self.verbosity = verbosity
1294 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001295 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001296
Damjan Marionf56b77a2016-10-03 19:44:57 +02001297 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001298 """
1299 Record a test succeeded result
1300
1301 :param test:
1302
1303 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001304 if self.current_test_case_info:
1305 self.current_test_case_info.logger.debug(
1306 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1307 test._testMethodName,
1308 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001309 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001310 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001311
juraj.linkescae64f82018-09-19 15:01:47 +02001312 self.send_result_through_pipe(test, PASS)
1313
Klement Sekeraf62ae122016-10-11 11:47:09 +02001314 def addSkip(self, test, reason):
1315 """
1316 Record a test skipped.
1317
1318 :param test:
1319 :param reason:
1320
1321 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001322 if self.current_test_case_info:
1323 self.current_test_case_info.logger.debug(
1324 "--- addSkip() %s.%s(%s) called, reason is %s" %
1325 (test.__class__.__name__, test._testMethodName,
1326 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001327 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001328 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001329
juraj.linkescae64f82018-09-19 15:01:47 +02001330 self.send_result_through_pipe(test, SKIP)
1331
juraj.linkes40dd73b2018-09-21 13:55:16 +02001332 def symlink_failed(self):
1333 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001334 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001335 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001336 link_path = os.path.join(
1337 failed_dir,
1338 '%s-FAILED' %
1339 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001340
1341 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001342 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001343 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001344 "os.symlink(%s, %s)" %
1345 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001346 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001347 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001348 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001349 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001350 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001351
Klement Sekeraf413bef2017-08-15 07:09:02 +02001352 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001353 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001354
juraj.linkescae64f82018-09-19 15:01:47 +02001355 def send_result_through_pipe(self, test, result):
1356 if hasattr(self, 'test_framework_result_pipe'):
1357 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001358 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001359 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001360
juraj.linkes40dd73b2018-09-21 13:55:16 +02001361 def log_error(self, test, err, fn_name):
1362 if self.current_test_case_info:
1363 if isinstance(test, unittest.suite._ErrorHolder):
1364 test_name = test.description
1365 else:
1366 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1367 test._testMethodName,
1368 test._testMethodDoc)
1369 self.current_test_case_info.logger.debug(
1370 "--- %s() %s called, err is %s" %
1371 (fn_name, test_name, err))
1372 self.current_test_case_info.logger.debug(
1373 "formatted exception is:\n%s" %
1374 "".join(format_exception(*err)))
1375
1376 def add_error(self, test, err, unittest_fn, error_type):
1377 if error_type == FAIL:
1378 self.log_error(test, err, 'addFailure')
1379 error_type_str = colorize("FAIL", RED)
1380 elif error_type == ERROR:
1381 self.log_error(test, err, 'addError')
1382 error_type_str = colorize("ERROR", RED)
1383 else:
1384 raise Exception('Error type %s cannot be used to record an '
1385 'error or a failure' % error_type)
1386
1387 unittest_fn(self, test, err)
1388 if self.current_test_case_info:
1389 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1390 (error_type_str,
1391 self.current_test_case_info.tempdir)
1392 self.symlink_failed()
1393 self.failed_test_cases_info.add(self.current_test_case_info)
1394 if is_core_present(self.current_test_case_info.tempdir):
1395 if not self.current_test_case_info.core_crash_test:
1396 if isinstance(test, unittest.suite._ErrorHolder):
1397 test_name = str(test)
1398 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001399 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001400 get_testcase_doc_name(test), test.id())
1401 self.current_test_case_info.core_crash_test = test_name
1402 self.core_crash_test_cases_info.add(
1403 self.current_test_case_info)
1404 else:
1405 self.result_string = '%s [no temp dir]' % error_type_str
1406
1407 self.send_result_through_pipe(test, error_type)
1408
Damjan Marionf56b77a2016-10-03 19:44:57 +02001409 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001410 """
1411 Record a test failed result
1412
1413 :param test:
1414 :param err: error message
1415
1416 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001417 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001418
Damjan Marionf56b77a2016-10-03 19:44:57 +02001419 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001420 """
1421 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001422
Klement Sekeraf62ae122016-10-11 11:47:09 +02001423 :param test:
1424 :param err: error message
1425
1426 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001427 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001428
Damjan Marionf56b77a2016-10-03 19:44:57 +02001429 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001430 """
1431 Get test description
1432
1433 :param test:
1434 :returns: test description
1435
1436 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001437 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001438
Damjan Marionf56b77a2016-10-03 19:44:57 +02001439 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001440 """
1441 Start a test
1442
1443 :param test:
1444
1445 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001446
1447 def print_header(test):
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001448 test_doc = getdoc(test)
1449 if not test_doc:
1450 raise Exception("No doc string for test '%s'" % test.id())
1451 test_title = test_doc.splitlines()[0]
1452 test_title_colored = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001453 if test.is_tagged_run_solo():
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001454 # long live PEP-8 and 80 char width limitation...
1455 c = YELLOW
1456 test_title_colored = colorize("SOLO RUN: " + test_title, c)
1457
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001458 # This block may overwrite the colorized title above,
1459 # but we want this to stand out and be fixed
1460 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
1461 c = RED
1462 w = "FIXME with VPP workers: "
1463 test_title_colored = colorize(w + test_title, c)
1464
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001465 if not hasattr(test.__class__, '_header_printed'):
1466 print(double_line_delim)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001467 print(test_title_colored)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001468 print(double_line_delim)
1469 test.__class__._header_printed = True
1470
1471 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001472 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001473 unittest.TestResult.startTest(self, test)
1474 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001475 self.stream.writeln(
1476 "Starting " + self.getDescription(test) + " ...")
1477 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001478
Damjan Marionf56b77a2016-10-03 19:44:57 +02001479 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001480 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001481 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001482
1483 :param test:
1484
1485 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001486 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001487
Damjan Marionf56b77a2016-10-03 19:44:57 +02001488 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001489 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001490 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001491 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001492 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001493 else:
Ole Troan0c629322019-11-28 14:48:44 +01001494 self.stream.writeln("%-68s %4.2f %s" %
1495 (self.getDescription(test),
1496 time.time() - self.start_test,
1497 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001498
1499 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001500
Damjan Marionf56b77a2016-10-03 19:44:57 +02001501 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001502 """
1503 Print errors from running the test case
1504 """
juraj.linkesabec0122018-11-16 17:28:56 +01001505 if len(self.errors) > 0 or len(self.failures) > 0:
1506 self.stream.writeln()
1507 self.printErrorList('ERROR', self.errors)
1508 self.printErrorList('FAIL', self.failures)
1509
1510 # ^^ that is the last output from unittest before summary
1511 if not self.runner.print_summary:
1512 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1513 self.stream = devnull
1514 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001515
Damjan Marionf56b77a2016-10-03 19:44:57 +02001516 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001517 """
1518 Print error list to the output stream together with error type
1519 and test case description.
1520
1521 :param flavour: error type
1522 :param errors: iterable errors
1523
1524 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001525 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001526 self.stream.writeln(double_line_delim)
1527 self.stream.writeln("%s: %s" %
1528 (flavour, self.getDescription(test)))
1529 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001530 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001531
1532
Damjan Marionf56b77a2016-10-03 19:44:57 +02001533class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001534 """
Klement Sekera104543f2017-02-03 07:29:43 +01001535 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001536 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001537
Klement Sekeraf62ae122016-10-11 11:47:09 +02001538 @property
1539 def resultclass(self):
1540 """Class maintaining the results of the tests"""
1541 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001542
juraj.linkes184870a2018-07-16 14:22:01 +02001543 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001544 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001545 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001546 # ignore stream setting here, use hard-coded stdout to be in sync
1547 # with prints from VppTestCase methods ...
1548 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1549 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001550 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001551 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001552
juraj.linkesabec0122018-11-16 17:28:56 +01001553 self.orig_stream = self.stream
1554 self.resultclass.test_framework_result_pipe = result_pipe
1555
1556 self.print_summary = print_summary
1557
1558 def _makeResult(self):
1559 return self.resultclass(self.stream,
1560 self.descriptions,
1561 self.verbosity,
1562 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001563
Damjan Marionf56b77a2016-10-03 19:44:57 +02001564 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001565 """
1566 Run the tests
1567
1568 :param test:
1569
1570 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001571 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001572
1573 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001574 if not self.print_summary:
1575 self.stream = self.orig_stream
1576 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001577 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001578
1579
1580class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001581 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1582 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001583 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001584 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001585 if hasattr(self, 'testcase') and self.testcase.debug_all:
1586 if self.testcase.debug_gdbserver:
1587 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1588 .format(port=self.testcase.gdbserver_port)] + args
1589 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1590 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001591 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001592 self.app_name = os.path.basename(self.app_bin)
1593 if hasattr(self, 'role'):
1594 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001595 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001596 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001597 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001598 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001599
Dave Wallace24564332019-10-21 02:53:14 +00001600 def wait_for_enter(self):
1601 if not hasattr(self, 'testcase'):
1602 return
1603 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1604 print()
1605 print(double_line_delim)
1606 print("Spawned GDB Server for '{app}' with PID: {pid}"
1607 .format(app=self.app_name, pid=self.process.pid))
1608 elif self.testcase.debug_all and self.testcase.debug_gdb:
1609 print()
1610 print(double_line_delim)
1611 print("Spawned '{app}' with PID: {pid}"
1612 .format(app=self.app_name, pid=self.process.pid))
1613 else:
1614 return
1615 print(single_line_delim)
1616 print("You can debug '{app}' using:".format(app=self.app_name))
1617 if self.testcase.debug_gdbserver:
1618 print("sudo gdb " + self.app_bin +
1619 " -ex 'target remote localhost:{port}'"
1620 .format(port=self.testcase.gdbserver_port))
1621 print("Now is the time to attach gdb by running the above "
1622 "command, set up breakpoints etc., then resume from "
1623 "within gdb by issuing the 'continue' command")
1624 self.testcase.gdbserver_port += 1
1625 elif self.testcase.debug_gdb:
1626 print("sudo gdb " + self.app_bin +
1627 " -ex 'attach {pid}'".format(pid=self.process.pid))
1628 print("Now is the time to attach gdb by running the above "
1629 "command and set up breakpoints etc., then resume from"
1630 " within gdb by issuing the 'continue' command")
1631 print(single_line_delim)
1632 input("Press ENTER to continue running the testcase...")
1633
Neale Ranns812ed392017-10-16 04:20:13 -07001634 def run(self):
1635 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001636 if not os.path.exists(executable) or not os.access(
1637 executable, os.F_OK | os.X_OK):
1638 # Exit code that means some system file did not exist,
1639 # could not be opened, or had some other kind of error.
1640 self.result = os.EX_OSFILE
1641 raise EnvironmentError(
1642 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001643 self.logger.debug("Running executable: '{app}'"
1644 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001645 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001646 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001647 env["CK_LOG_FILE_NAME"] = "-"
1648 self.process = subprocess.Popen(
1649 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1650 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001651 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001652 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001653 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001654 self.logger.info("Return code is `%s'" % self.process.returncode)
1655 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001656 self.logger.info("Executable `{app}' wrote to stdout:"
1657 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001658 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001659 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001660 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001661 self.logger.info("Executable `{app}' wrote to stderr:"
1662 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001663 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001664 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001665 self.logger.info(single_line_delim)
1666 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001667
Klement Sekera6aa58b72019-05-16 14:34:55 +02001668
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001669if __name__ == '__main__':
1670 pass