blob: b7004613edd749b521d8ab83e11ae1c7e688cf0b [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
4import gc
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05005import logging
Paul Vinciguerra72f00042018-11-25 11:05:13 -08006import sys
Ole Trøan162989e2018-11-26 10:27:50 +00007import os
8import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04009import signal
Paul Vinciguerrad6f22172020-12-05 22:39:14 +000010import subprocess
Ole Trøan162989e2018-11-26 10:27:50 +000011import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +020012import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020013import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080014import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000015import random
16import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080017import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010018import platform
Ole Trøan162989e2018-11-26 10:27:50 +000019from collections import deque
20from threading import Thread, Event
21from inspect import getdoc, isclass
22from traceback import format_exception
23from logging import FileHandler, DEBUG, Formatter
Andrew Yourtchenko06f32812021-01-14 10:19:08 +000024from enum import Enum
Klement Sekera558ceab2021-04-08 19:37:41 +020025from abc import ABC, abstractmethod
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070026
27import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000028from scapy.packet import Raw
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040029import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080030from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010031from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000032from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070033from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000034from vpp_papi_provider import VppPapiProvider
Neale Ranns6197cb72021-06-03 14:43:21 +000035from vpp_papi import VppEnum
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050036import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000037from vpp_papi.vpp_stats import VPPStats
Ole Troan4376ab22021-03-03 10:40:05 +010038from vpp_papi.vpp_transport_socket import VppTransportSocketIOError
Ole Trøan162989e2018-11-26 10:27:50 +000039from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
40 get_logger, colorize
41from vpp_object import VppObjectRegistry
42from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020043from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
44from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
45from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080046
Klement Sekera558ceab2021-04-08 19:37:41 +020047from cpu_config import available_cpus, num_cpus, max_vpp_cpus
48
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050049logger = logging.getLogger(__name__)
50
51# Set up an empty logger for the testcase that can be overridden as necessary
52null_logger = logging.getLogger('VppTestCase')
53null_logger.addHandler(logging.NullHandler())
54
juraj.linkescae64f82018-09-19 15:01:47 +020055PASS = 0
56FAIL = 1
57ERROR = 2
58SKIP = 3
59TEST_RUN = 4
Klement Sekera558ceab2021-04-08 19:37:41 +020060SKIP_CPU_SHORTAGE = 5
juraj.linkescae64f82018-09-19 15:01:47 +020061
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040062
63class BoolEnvironmentVariable(object):
64
65 def __init__(self, env_var_name, default='n', true_values=None):
66 self.name = env_var_name
67 self.default = default
68 self.true_values = true_values if true_values is not None else \
69 ("y", "yes", "1")
70
71 def __bool__(self):
72 return os.getenv(self.name, self.default).lower() in self.true_values
73
74 if sys.version_info[0] == 2:
75 __nonzero__ = __bool__
76
77 def __repr__(self):
78 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
79 (self.name, self.default, self.true_values)
80
81
82debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
83if debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010084 import debug_internal
85
Klement Sekeraf62ae122016-10-11 11:47:09 +020086"""
87 Test framework module.
88
89 The module provides a set of tools for constructing and running tests and
90 representing the results.
91"""
92
Klement Sekeraf62ae122016-10-11 11:47:09 +020093
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040094class VppDiedError(Exception):
95 """ exception for reporting that the subprocess has died."""
96
97 signals_by_value = {v: k for k, v in signal.__dict__.items() if
98 k.startswith('SIG') and not k.startswith('SIG_')}
99
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400100 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400101 self.rv = rv
102 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400103 self.testcase = testcase
104 self.method_name = method_name
105
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400106 try:
107 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400108 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400109 pass
110
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400111 if testcase is None and method_name is None:
112 in_msg = ''
113 else:
Klement Sekera79a31db2021-03-12 18:16:10 +0100114 in_msg = ' while running %s.%s' % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400115
Klement Sekera79a31db2021-03-12 18:16:10 +0100116 if self.rv:
117 msg = "VPP subprocess died unexpectedly%s with return code: %d%s."\
118 % (in_msg, self.rv, ' [%s]' %
119 (self.signal_name if
120 self.signal_name is not None else ''))
121 else:
122 msg = "VPP subprocess died unexpectedly%s." % in_msg
123
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400124 super(VppDiedError, self).__init__(msg)
125
126
Damjan Marionf56b77a2016-10-03 19:44:57 +0200127class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200128 """Private class to create packet info object.
129
130 Help process information about the next packet.
131 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200132 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100133 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200134 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100135 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200136 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100137 #: Store the index of the destination packet generator interface
138 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200139 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100140 #: Store expected ip version
141 ip = -1
142 #: Store expected upper protocol
143 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100144 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200145 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200146
Matej Klotton16a14cd2016-12-07 15:09:13 +0100147 def __eq__(self, other):
148 index = self.index == other.index
149 src = self.src == other.src
150 dst = self.dst == other.dst
151 data = self.data == other.data
152 return index and src and dst and data
153
Klement Sekeraf62ae122016-10-11 11:47:09 +0200154
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100155def pump_output(testclass):
156 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100157 stdout_fragment = ""
158 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400159 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100160 readable = select.select([testclass.vpp.stdout.fileno(),
161 testclass.vpp.stderr.fileno(),
162 testclass.pump_thread_wakeup_pipe[0]],
163 [], [])[0]
164 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100165 read = os.read(testclass.vpp.stdout.fileno(), 102400)
166 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200167 split = read.decode('ascii',
168 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100169 if len(stdout_fragment) > 0:
170 split[0] = "%s%s" % (stdout_fragment, split[0])
171 if len(split) > 0 and split[-1].endswith("\n"):
172 limit = None
173 else:
174 limit = -1
175 stdout_fragment = split[-1]
176 testclass.vpp_stdout_deque.extend(split[:limit])
177 if not testclass.cache_vpp_output:
178 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100179 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100180 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100181 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100182 read = os.read(testclass.vpp.stderr.fileno(), 102400)
183 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200184 split = read.decode('ascii',
185 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100186 if len(stderr_fragment) > 0:
187 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200188 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100189 limit = None
190 else:
191 limit = -1
192 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200193
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100194 testclass.vpp_stderr_deque.extend(split[:limit])
195 if not testclass.cache_vpp_output:
196 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100197 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100198 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800199 # ignoring the dummy pipe here intentionally - the
200 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200201
202
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800203def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400204 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100205
Klement Sekera6aa58b72019-05-16 14:34:55 +0200206
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800207is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100208
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800209
210def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100211 return platform.machine() == 'aarch64'
212
Klement Sekera6aa58b72019-05-16 14:34:55 +0200213
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800214is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100215
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800216
217def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400218 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100219
Klement Sekera6aa58b72019-05-16 14:34:55 +0200220
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800221running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100222
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800223
Dave Barachd498c9e2020-03-10 16:59:39 -0400224def _running_gcov_tests():
225 return BoolEnvironmentVariable("GCOV_TESTS")
226
227
228running_gcov_tests = _running_gcov_tests()
229
230
Klement Sekera558ceab2021-04-08 19:37:41 +0200231def get_environ_vpp_worker_count():
232 worker_config = os.getenv("VPP_WORKER_CONFIG", None)
233 if worker_config:
234 elems = worker_config.split(" ")
235 if elems[0] != "workers" or len(elems) != 2:
236 raise ValueError("Wrong VPP_WORKER_CONFIG == '%s' value." %
237 worker_config)
238 return int(elems[1])
239 else:
240 return 0
241
242
243environ_vpp_worker_count = get_environ_vpp_worker_count()
244
245
Klement Sekera909a6a12017-08-08 04:33:53 +0200246class KeepAliveReporter(object):
247 """
248 Singleton object which reports test start to parent process
249 """
250 _shared_state = {}
251
252 def __init__(self):
253 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800254 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200255
256 @property
257 def pipe(self):
258 return self._pipe
259
260 @pipe.setter
261 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800262 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200263 raise Exception("Internal error - pipe should only be set once.")
264 self._pipe = pipe
265
juraj.linkes40dd73b2018-09-21 13:55:16 +0200266 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200267 """
268 Write current test tmpdir & desc to keep-alive pipe to signal liveness
269 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200270 if self.pipe is None:
271 # if not running forked..
272 return
273
Klement Sekera909a6a12017-08-08 04:33:53 +0200274 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200275 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200276 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200277 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200278
Dave Wallacee2efd122017-09-30 22:04:21 -0400279 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200280
281
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000282class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000283 # marks the suites that must run at the end
284 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000285 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000286 # marks the suites broken on VPP multi-worker
287 FIXME_VPP_WORKERS = 2
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000288
289
290def create_tag_decorator(e):
291 def decorator(cls):
292 try:
293 cls.test_tags.append(e)
294 except AttributeError:
295 cls.test_tags = [e]
296 return cls
297 return decorator
298
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000299
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000300tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000301tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000302
303
Klement Sekerae2636852021-03-16 12:52:12 +0100304class DummyVpp:
305 returncode = None
306 pid = 0xcafebafe
307
308 def poll(self):
309 pass
310
311 def terminate(self):
312 pass
313
314
Klement Sekera558ceab2021-04-08 19:37:41 +0200315class CPUInterface(ABC):
316 cpus = []
317 skipped_due_to_cpu_lack = False
318
319 @classmethod
320 @abstractmethod
321 def get_cpus_required(cls):
322 pass
323
324 @classmethod
325 def assign_cpus(cls, cpus):
326 cls.cpus = cpus
327
328
329class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100330 """This subclass is a base class for VPP test cases that are implemented as
331 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200332 """
333
Arthur de Kerhordb023802021-03-11 10:26:54 -0800334 extra_vpp_statseg_config = ""
Ole Troana45dc072018-12-21 16:04:22 +0100335 extra_vpp_punt_config = []
336 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500337 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400338 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100339
Klement Sekeraf62ae122016-10-11 11:47:09 +0200340 @property
341 def packet_infos(self):
342 """List of packet infos"""
343 return self._packet_infos
344
Klement Sekeradab231a2016-12-21 08:50:14 +0100345 @classmethod
346 def get_packet_count_for_if_idx(cls, dst_if_index):
347 """Get the number of packet info for specified destination if index"""
348 if dst_if_index in cls._packet_count_for_dst_if_idx:
349 return cls._packet_count_for_dst_if_idx[dst_if_index]
350 else:
351 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200352
353 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000354 def has_tag(cls, tag):
355 """ if the test case has a given tag - return true """
356 try:
357 return tag in cls.test_tags
358 except AttributeError:
359 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000360 return False
361
362 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000363 def is_tagged_run_solo(cls):
364 """ if the test case class is timing-sensitive - return true """
365 return cls.has_tag(TestCaseTag.RUN_SOLO)
366
367 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200368 def instance(cls):
369 """Return the instance of this testcase"""
370 return cls.test_instance
371
Damjan Marionf56b77a2016-10-03 19:44:57 +0200372 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200373 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000374 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200375 cls.debug_core = False
376 cls.debug_gdb = False
377 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000378 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100379 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200380 if d is None:
381 return
382 dl = d.lower()
383 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200384 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000385 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200386 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000387 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200388 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100389 elif dl == "attach":
390 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200391 else:
392 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000393 if dl == "gdb-all" or dl == "gdbserver-all":
394 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200395
Klement Sekera558ceab2021-04-08 19:37:41 +0200396 @classmethod
397 def get_vpp_worker_count(cls):
398 if not hasattr(cls, "vpp_worker_count"):
399 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
400 cls.vpp_worker_count = 0
401 else:
402 cls.vpp_worker_count = environ_vpp_worker_count
403 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200404
Klement Sekera558ceab2021-04-08 19:37:41 +0200405 @classmethod
406 def get_cpus_required(cls):
407 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200408
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800409 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200410 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200411 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400412 cls.step = BoolEnvironmentVariable('STEP')
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400413 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100414 c = os.getenv("CACHE_OUTPUT", "1")
415 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekerab8c72a42018-11-08 11:21:39 +0100416 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
417 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400418 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100419 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
420 plugin_path = None
421 if cls.plugin_path is not None:
422 if cls.extern_plugin_path is not None:
423 plugin_path = "%s:%s" % (
424 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100425 else:
426 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100427 elif cls.extern_plugin_path is not None:
428 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100429 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100430 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100431 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100432 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100433 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100434 if size is not None:
435 coredump_size = "coredump-size %s" % size
436 if coredump_size is None:
437 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200438
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000439 default_variant = os.getenv("VARIANT")
440 if default_variant is not None:
441 default_variant = "defaults { %s 100 }" % default_variant
442 else:
443 default_variant = ""
444
Dave Barach77841402020-04-29 17:04:10 -0400445 api_fuzzing = os.getenv("API_FUZZ")
446 if api_fuzzing is None:
447 api_fuzzing = 'off'
448
Klement Sekera8d815022021-03-15 16:58:10 +0100449 cls.vpp_cmdline = [
450 cls.vpp_bin,
451 "unix", "{", "nodaemon", debug_cli, "full-coredump",
452 coredump_size, "runtime-dir", cls.tempdir, "}",
453 "api-trace", "{", "on", "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100454 "api-segment", "{", "prefix", cls.get_api_segment_prefix(), "}",
Klement Sekera558ceab2021-04-08 19:37:41 +0200455 "cpu", "{", "main-core", str(cls.cpus[0]), ]
456 if cls.get_vpp_worker_count():
457 cls.vpp_cmdline.extend([
458 "corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])])
Klement Sekera8d815022021-03-15 16:58:10 +0100459 cls.vpp_cmdline.extend([
460 "}",
461 "physmem", "{", "max-size", "32m", "}",
Arthur de Kerhordb023802021-03-11 10:26:54 -0800462 "statseg", "{", "socket-name", cls.get_stats_sock_path(),
463 cls.extra_vpp_statseg_config, "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100464 "socksvr", "{", "socket-name", cls.get_api_sock_path(), "}",
Klement Sekera8d815022021-03-15 16:58:10 +0100465 "node { ", default_variant, "}",
466 "api-fuzz {", api_fuzzing, "}",
467 "plugins", "{", "plugin", "dpdk_plugin.so", "{", "disable", "}",
468 "plugin", "rdma_plugin.so", "{", "disable", "}",
469 "plugin", "lisp_unittest_plugin.so", "{", "enable", "}",
470 "plugin", "unittest_plugin.so", "{", "enable", "}"
471 ] + cls.extra_vpp_plugin_config + ["}", ])
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000472
Ole Troana45dc072018-12-21 16:04:22 +0100473 if cls.extra_vpp_punt_config is not None:
474 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100475 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100476 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400477 if cls.test_plugin_path is not None:
478 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
479
Klement Sekerae2636852021-03-16 12:52:12 +0100480 if not cls.debug_attach:
481 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
482 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200483
484 @classmethod
485 def wait_for_enter(cls):
486 if cls.debug_gdbserver:
487 print(double_line_delim)
488 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
489 elif cls.debug_gdb:
490 print(double_line_delim)
491 print("Spawned VPP with PID: %d" % cls.vpp.pid)
492 else:
493 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
494 return
495 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000496 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200497 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400498 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000499 " -ex 'target remote localhost:{port}'"
500 .format(port=cls.gdbserver_port))
501 print("Now is the time to attach gdb by running the above "
502 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200503 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000504 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200505 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400506 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000507 print("Now is the time to attach gdb by running the above "
508 "command and set up breakpoints etc., then resume VPP from"
509 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200510 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800511 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200512
513 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100514 def attach_vpp(cls):
515 cls.vpp = DummyVpp()
516
517 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200518 def run_vpp(cls):
Klement Sekera558ceab2021-04-08 19:37:41 +0200519 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200520 cmdline = cls.vpp_cmdline
521
522 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100523 gdbserver = '/usr/bin/gdbserver'
Klement Sekera558ceab2021-04-08 19:37:41 +0200524 if not os.path.isfile(gdbserver) or\
Klement Sekera931be3a2016-11-03 05:36:01 +0100525 not os.access(gdbserver, os.X_OK):
526 raise Exception("gdbserver binary '%s' does not exist or is "
527 "not executable" % gdbserver)
528
Dave Wallace24564332019-10-21 02:53:14 +0000529 cmdline = [gdbserver, 'localhost:{port}'
530 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200531 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
532
Klement Sekera931be3a2016-11-03 05:36:01 +0100533 try:
534 cls.vpp = subprocess.Popen(cmdline,
535 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100536 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800537 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800538 cls.logger.critical("Subprocess returned with non-0 return code: ("
539 "%s)", e.returncode)
540 raise
541 except OSError as e:
542 cls.logger.critical("Subprocess returned with OS error: "
543 "(%s) %s", e.errno, e.strerror)
544 raise
545 except Exception as e:
546 cls.logger.exception("Subprocess returned unexpected from "
547 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100548 raise
549
Klement Sekera277b89c2016-10-28 13:20:27 +0200550 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100551
Damjan Marionf56b77a2016-10-03 19:44:57 +0200552 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000553 def wait_for_coredump(cls):
554 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400555 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000556 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400557 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000558 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400559 ok = False
560 while time.time() < deadline:
561 cls.sleep(1)
562 size = curr_size
563 curr_size = os.path.getsize(corefile)
564 if size == curr_size:
565 ok = True
566 break
567 if not ok:
568 cls.logger.error("Timed out waiting for coredump to complete:"
569 " %s", corefile)
570 else:
571 cls.logger.error("Coredump complete: %s, size %d",
572 corefile, curr_size)
573
574 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100575 def get_stats_sock_path(cls):
576 return "%s/stats.sock" % cls.tempdir
577
578 @classmethod
579 def get_api_sock_path(cls):
580 return "%s/api.sock" % cls.tempdir
581
582 @classmethod
583 def get_api_segment_prefix(cls):
584 return os.path.basename(cls.tempdir) # Only used for VAPI
585
586 @classmethod
587 def get_tempdir(cls):
588 if cls.debug_attach:
589 return os.getenv("VPP_IN_GDB_TMP_DIR",
590 "/tmp/unittest-attach-gdb")
591 else:
592 return tempfile.mkdtemp(prefix='vpp-unittest-%s-' % cls.__name__)
593
594 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200595 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200596 """
597 Perform class setup before running the testcase
598 Remove shared memory files, start vpp and connect the vpp-api
599 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800600 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100601 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000602 seed = os.environ["RND_SEED"]
603 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100604 if hasattr(cls, 'parallel_handler'):
605 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100606 cls.logger.propagate = False
Klement Sekerae2636852021-03-16 12:52:12 +0100607 d = os.getenv("DEBUG", None)
608 cls.set_debug_flags(d)
609 cls.tempdir = cls.get_tempdir()
Klement Sekera027dbd52017-04-11 06:01:53 +0200610 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
611 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100612 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
613 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200614 cls.file_handler.setLevel(DEBUG)
615 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100616 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200617 os.chdir(cls.tempdir)
Ole Troan4376ab22021-03-03 10:40:05 +0100618 cls.logger.info("Temporary dir is %s, api socket is %s",
Klement Sekerae2636852021-03-16 12:52:12 +0100619 cls.tempdir, cls.get_api_sock_path())
620 cls.logger.debug("Random seed is %s", seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200621 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100622 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200623 cls._pcaps = []
624 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200625 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100626 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100627 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200628 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200629 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200630 # need to catch exceptions here because if we raise, then the cleanup
631 # doesn't get called and we might end with a zombie vpp
632 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100633 if cls.debug_attach:
634 cls.attach_vpp()
635 else:
636 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200637 cls.reporter.send_keep_alive(cls, 'setUpClass')
638 VppTestResult.current_test_case_info = TestCaseInfo(
639 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100640 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100641 cls.vpp_stderr_deque = deque()
Klement Sekerae2636852021-03-16 12:52:12 +0100642 if not cls.debug_attach:
643 cls.pump_thread_stop_flag = Event()
644 cls.pump_thread_wakeup_pipe = os.pipe()
645 cls.pump_thread = Thread(target=pump_output, args=(cls,))
646 cls.pump_thread.daemon = True
647 cls.pump_thread.start()
648 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400649 cls.vapi_response_timeout = 0
Ole Troan4376ab22021-03-03 10:40:05 +0100650 cls.vapi = VppPapiProvider(cls.__name__, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400651 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100652 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400653 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100654 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400655 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100656 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100657 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200658 try:
659 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100660 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200661 cls.vpp_startup_failed = True
662 cls.logger.critical(
663 "VPP died shortly after startup, check the"
664 " output to standard error for possible cause")
665 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100666 try:
667 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100668 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500669 cls.logger.debug("Exception connecting to vapi: %s" % e)
670 cls.vapi.disconnect()
671
Klement Sekera085f5c02016-11-24 01:59:16 +0100672 if cls.debug_gdbserver:
673 print(colorize("You're running VPP inside gdbserver but "
674 "VPP-API connection failed, did you forget "
675 "to 'continue' VPP from within gdb?", RED))
Ole Troan4376ab22021-03-03 10:40:05 +0100676 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100677 if cls.debug_attach:
678 last_line = cls.vapi.cli("show thread").split("\n")[-2]
679 cls.vpp_worker_count = int(last_line.split(" ")[0])
680 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400681 except vpp_papi.VPPRuntimeError as e:
682 cls.logger.debug("%s" % e)
683 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100684 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000685 except Exception as e:
686 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400687 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100688 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200689
Damjan Marionf56b77a2016-10-03 19:44:57 +0200690 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500691 def _debug_quit(cls):
692 if (cls.debug_gdbserver or cls.debug_gdb):
693 try:
694 cls.vpp.poll()
695
696 if cls.vpp.returncode is None:
697 print()
698 print(double_line_delim)
699 print("VPP or GDB server is still running")
700 print(single_line_delim)
701 input("When done debugging, press ENTER to kill the "
702 "process and finish running the testcase...")
703 except AttributeError:
704 pass
705
706 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200707 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200708 """
709 Disconnect vpp-api, kill vpp and cleanup shared memory files
710 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500711 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200712
juraj.linkes184870a2018-07-16 14:22:01 +0200713 # first signal that we want to stop the pump thread, then wake it up
714 if hasattr(cls, 'pump_thread_stop_flag'):
715 cls.pump_thread_stop_flag.set()
716 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100717 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100718 if hasattr(cls, 'pump_thread'):
719 cls.logger.debug("Waiting for pump thread to stop")
720 cls.pump_thread.join()
721 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500722 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100723 cls.vpp_stderr_reader_thread.join()
724
Klement Sekeraf62ae122016-10-11 11:47:09 +0200725 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100726 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100727 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700728 cls.logger.debug("Disconnecting class vapi client on %s",
729 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100730 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700731 cls.logger.debug("Deleting class vapi attribute on %s",
732 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100733 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200734 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100735 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000736 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100737 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400738 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100739 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100740 try:
741 outs, errs = cls.vpp.communicate(timeout=5)
742 except subprocess.TimeoutExpired:
743 cls.vpp.kill()
744 outs, errs = cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700745 cls.logger.debug("Deleting class vpp attribute on %s",
746 cls.__name__)
Klement Sekerae2636852021-03-16 12:52:12 +0100747 if not cls.debug_attach:
748 cls.vpp.stdout.close()
749 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200750 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200751
Klement Sekera3747c752017-04-10 06:30:17 +0200752 if cls.vpp_startup_failed:
753 stdout_log = cls.logger.info
754 stderr_log = cls.logger.critical
755 else:
756 stdout_log = cls.logger.info
757 stderr_log = cls.logger.info
758
Klement Sekerae4504c62016-12-08 10:16:41 +0100759 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200760 stdout_log(single_line_delim)
761 stdout_log('VPP output to stdout while running %s:', cls.__name__)
762 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100763 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200764 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
765 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200766 stdout_log('\n%s', vpp_output)
767 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200768
Klement Sekerae4504c62016-12-08 10:16:41 +0100769 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200770 stderr_log(single_line_delim)
771 stderr_log('VPP output to stderr while running %s:', cls.__name__)
772 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100773 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200774 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
775 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200776 stderr_log('\n%s', vpp_output)
777 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200778
Damjan Marionf56b77a2016-10-03 19:44:57 +0200779 @classmethod
780 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200781 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700782 cls.logger.debug("--- tearDownClass() for %s called ---" %
783 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200784 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200785 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200786 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100787 cls.reset_packet_infos()
788 if debug_framework:
789 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200790
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700791 def show_commands_at_teardown(self):
792 """ Allow subclass specific teardown logging additions."""
793 self.logger.info("--- No test specific show commands provided. ---")
794
Damjan Marionf56b77a2016-10-03 19:44:57 +0200795 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200796 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100797 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
798 (self.__class__.__name__, self._testMethodName,
799 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700800
801 try:
802 if not self.vpp_dead:
803 self.logger.debug(self.vapi.cli("show trace max 1000"))
804 self.logger.info(self.vapi.ppcli("show interface"))
805 self.logger.info(self.vapi.ppcli("show hardware"))
806 self.logger.info(self.statistics.set_errors_str())
807 self.logger.info(self.vapi.ppcli("show run"))
808 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400809 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700810 self.logger.info("Logging testcase specific show commands.")
811 self.show_commands_at_teardown()
812 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500813 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000814 m = self._testMethodName
815 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500816 tmp_api_trace = "/tmp/%s" % api_trace
817 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
818 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
819 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
820 vpp_api_trace_log))
821 os.rename(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100822 except VppTransportSocketIOError:
823 self.logger.debug("VppTransportSocketIOError: Vpp dead. "
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700824 "Cannot log show commands.")
825 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100826 else:
827 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200828
Damjan Marionf56b77a2016-10-03 19:44:57 +0200829 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200830 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800831 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200832 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100833 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400834 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
835 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100836 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100837 self.vpp_stdout_deque.append(
838 "--- test setUp() for %s.%s(%s) starts here ---\n" %
839 (self.__class__.__name__, self._testMethodName,
840 self._testMethodDoc))
841 self.vpp_stderr_deque.append(
842 "--- test setUp() for %s.%s(%s) starts here ---\n" %
843 (self.__class__.__name__, self._testMethodName,
844 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200845 self.vapi.cli("clear trace")
846 # store the test instance inside the test class - so that objects
847 # holding the class can access instance methods (like assertEqual)
848 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200849
Damjan Marionf56b77a2016-10-03 19:44:57 +0200850 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200851 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200852 """
853 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200854
Klement Sekera75e7d132017-09-20 08:26:30 +0200855 :param interfaces: iterable interface indexes (if None,
856 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200857
Klement Sekeraf62ae122016-10-11 11:47:09 +0200858 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200859 if interfaces is None:
860 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200861 for i in interfaces:
862 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200863
Damjan Marionf56b77a2016-10-03 19:44:57 +0200864 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200865 def register_pcap(cls, intf, worker):
866 """ Register a pcap in the testclass """
Klement Sekera9225dee2016-12-12 08:36:58 +0100867 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200868 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100869
870 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000871 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400872 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
873 # returns float("2.190522")
874 timestr = cls.vapi.cli('show clock')
875 head, sep, tail = timestr.partition(',')
876 head, sep, tail = head.partition('Time now')
877 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000878
879 @classmethod
880 def sleep_on_vpp_time(cls, sec):
881 """ Sleep according to time in VPP world """
882 # On a busy system with many processes
883 # we might end up with VPP time being slower than real world
884 # So take that into account when waiting for VPP to do something
885 start_time = cls.get_vpp_time()
886 while cls.get_vpp_time() - start_time < sec:
887 cls.sleep(0.1)
888
889 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100890 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000891 """ Enable the PG, wait till it is done, then clean up """
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200892 for (intf, worker) in cls._old_pcaps:
893 intf.rename_old_pcap_file(intf.get_in_path(worker),
894 intf.in_history_counter)
895 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100896 if trace:
897 cls.vapi.cli("clear trace")
898 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200899 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000900 # PG, when starts, runs to completion -
901 # so let's avoid a race condition,
902 # and wait a little till it's done.
903 # Then clean it up - and then be gone.
904 deadline = time.time() + 300
905 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
906 cls.sleep(0.01) # yield
907 if time.time() > deadline:
908 cls.logger.error("Timeout waiting for pg to stop")
909 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200910 for intf, worker in cls._pcaps:
Klement Sekera7ba9fae2021-03-31 13:36:38 +0200911 cls.vapi.cli('packet-generator delete %s' %
912 intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200913 cls._old_pcaps = cls._pcaps
914 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200915
Damjan Marionf56b77a2016-10-03 19:44:57 +0200916 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000917 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0,
918 mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200919 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100920 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200921
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100922 :param interfaces: iterable indexes of the interfaces.
923 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200924
Klement Sekeraf62ae122016-10-11 11:47:09 +0200925 """
926 result = []
927 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +0000928 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200929 setattr(cls, intf.name, intf)
930 result.append(intf)
931 cls.pg_interfaces = result
932 return result
933
Matej Klotton0178d522016-11-04 11:11:44 +0100934 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000935 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
936 pgmode = VppEnum.vl_api_pg_interface_mode_t
937 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
938 pgmode.PG_API_MODE_IP4)
939
940 @classmethod
941 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
942 pgmode = VppEnum.vl_api_pg_interface_mode_t
943 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
944 pgmode.PG_API_MODE_IP6)
945
946 @classmethod
947 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
948 pgmode = VppEnum.vl_api_pg_interface_mode_t
949 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
950 pgmode.PG_API_MODE_ETHERNET)
951
952 @classmethod
953 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
954 pgmode = VppEnum.vl_api_pg_interface_mode_t
955 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
956 pgmode.PG_API_MODE_ETHERNET)
957
958 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200959 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100960 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100961 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100962
Klement Sekerab9ef2732018-06-24 22:49:33 +0200963 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100964 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100965 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200966 result = [VppLoInterface(cls) for i in range(count)]
967 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100968 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100969 cls.lo_interfaces = result
970 return result
971
Neale Ranns192b13f2019-03-15 02:16:20 -0700972 @classmethod
973 def create_bvi_interfaces(cls, count):
974 """
975 Create BVI interfaces.
976
977 :param count: number of interfaces created.
978 :returns: List of created interfaces.
979 """
980 result = [VppBviInterface(cls) for i in range(count)]
981 for intf in result:
982 setattr(cls, intf.name, intf)
983 cls.bvi_interfaces = result
984 return result
985
Damjan Marionf56b77a2016-10-03 19:44:57 +0200986 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200987 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200988 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200989 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200990 NOTE: Currently works only when Raw layer is present.
991
992 :param packet: packet
993 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200994 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200995
996 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200997 packet_len = len(packet) + 4
998 extend = size - packet_len
999 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +02001000 num = (extend // len(padding)) + 1
1001 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +02001002
Klement Sekeradab231a2016-12-21 08:50:14 +01001003 @classmethod
1004 def reset_packet_infos(cls):
1005 """ Reset the list of packet info objects and packet counts to zero """
1006 cls._packet_infos = {}
1007 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +02001008
Klement Sekeradab231a2016-12-21 08:50:14 +01001009 @classmethod
1010 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001011 """
1012 Create packet info object containing the source and destination indexes
1013 and add it to the testcase's packet info list
1014
Klement Sekeradab231a2016-12-21 08:50:14 +01001015 :param VppInterface src_if: source interface
1016 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +02001017
1018 :returns: _PacketInfo object
1019
1020 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001021 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +01001022 info.index = len(cls._packet_infos)
1023 info.src = src_if.sw_if_index
1024 info.dst = dst_if.sw_if_index
1025 if isinstance(dst_if, VppSubInterface):
1026 dst_idx = dst_if.parent.sw_if_index
1027 else:
1028 dst_idx = dst_if.sw_if_index
1029 if dst_idx in cls._packet_count_for_dst_if_idx:
1030 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1031 else:
1032 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1033 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001034 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001035
Damjan Marionf56b77a2016-10-03 19:44:57 +02001036 @staticmethod
1037 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001038 """
1039 Convert _PacketInfo object to packet payload
1040
1041 :param info: _PacketInfo object
1042
1043 :returns: string containing serialized data from packet info
1044 """
Pavel Kotucek59dda062017-03-02 15:22:47 +01001045 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
1046 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001047
Damjan Marionf56b77a2016-10-03 19:44:57 +02001048 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001049 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001050 """
1051 Convert packet payload to _PacketInfo object
1052
1053 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001054 :type payload: <class 'scapy.packet.Raw'>
1055 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001056 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001057 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001058 :returns: _PacketInfo object containing de-serialized data from payload
1059
1060 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001061 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001062 info = _PacketInfo()
1063 info.index = int(numbers[0])
1064 info.src = int(numbers[1])
1065 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +01001066 info.ip = int(numbers[3])
1067 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +02001068 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001069
Damjan Marionf56b77a2016-10-03 19:44:57 +02001070 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001071 """
1072 Iterate over the packet info list stored in the testcase
1073 Start iteration with first element if info is None
1074 Continue based on index in info if info is specified
1075
1076 :param info: info or None
1077 :returns: next info in list or None if no more infos
1078 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001079 if info is None:
1080 next_index = 0
1081 else:
1082 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001083 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001084 return None
1085 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001086 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001087
Klement Sekeraf62ae122016-10-11 11:47:09 +02001088 def get_next_packet_info_for_interface(self, src_index, info):
1089 """
1090 Search the packet info list for the next packet info with same source
1091 interface index
1092
1093 :param src_index: source interface index to search for
1094 :param info: packet info - where to start the search
1095 :returns: packet info or None
1096
1097 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001098 while True:
1099 info = self.get_next_packet_info(info)
1100 if info is None:
1101 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001102 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001103 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001104
Klement Sekeraf62ae122016-10-11 11:47:09 +02001105 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1106 """
1107 Search the packet info list for the next packet info with same source
1108 and destination interface indexes
1109
1110 :param src_index: source interface index to search for
1111 :param dst_index: destination interface index to search for
1112 :param info: packet info - where to start the search
1113 :returns: packet info or None
1114
1115 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001116 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001117 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001118 if info is None:
1119 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001120 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001121 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001122
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001123 def assert_equal(self, real_value, expected_value, name_or_class=None):
1124 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001125 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001126 return
1127 try:
1128 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1129 msg = msg % (getdoc(name_or_class).strip(),
1130 real_value, str(name_or_class(real_value)),
1131 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001132 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001133 msg = "Invalid %s: %s does not match expected value %s" % (
1134 name_or_class, real_value, expected_value)
1135
1136 self.assertEqual(real_value, expected_value, msg)
1137
Klement Sekerab17dd962017-01-09 07:43:48 +01001138 def assert_in_range(self,
1139 real_value,
1140 expected_min,
1141 expected_max,
1142 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001143 if name is None:
1144 msg = None
1145 else:
1146 msg = "Invalid %s: %s out of range <%s,%s>" % (
1147 name, real_value, expected_min, expected_max)
1148 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1149
Klement Sekerad81ae412018-05-16 10:52:54 +02001150 def assert_packet_checksums_valid(self, packet,
1151 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001152 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001153 udp_layers = ['UDP', 'UDPerror']
1154 checksum_fields = ['cksum', 'chksum']
1155 checksums = []
1156 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001157 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001158 while True:
1159 layer = temp.getlayer(counter)
1160 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001161 layer = layer.copy()
1162 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001163 for cf in checksum_fields:
1164 if hasattr(layer, cf):
1165 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001166 0 == getattr(layer, cf) and \
1167 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001168 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001169 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001170 checksums.append((counter, cf))
1171 else:
1172 break
1173 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001174 if 0 == len(checksums):
1175 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001176 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001177 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001178 calc_sum = getattr(temp[layer], cf)
1179 self.assert_equal(
1180 getattr(received[layer], cf), calc_sum,
1181 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1182 self.logger.debug(
1183 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1184 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001185
1186 def assert_checksum_valid(self, received_packet, layer,
1187 field_name='chksum',
1188 ignore_zero_checksum=False):
1189 """ Check checksum of received packet on given layer """
1190 received_packet_checksum = getattr(received_packet[layer], field_name)
1191 if ignore_zero_checksum and 0 == received_packet_checksum:
1192 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001193 recalculated = received_packet.__class__(
1194 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001195 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001196 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001197 self.assert_equal(received_packet_checksum,
1198 getattr(recalculated[layer], field_name),
1199 "packet checksum on layer: %s" % layer)
1200
1201 def assert_ip_checksum_valid(self, received_packet,
1202 ignore_zero_checksum=False):
1203 self.assert_checksum_valid(received_packet, 'IP',
1204 ignore_zero_checksum=ignore_zero_checksum)
1205
1206 def assert_tcp_checksum_valid(self, received_packet,
1207 ignore_zero_checksum=False):
1208 self.assert_checksum_valid(received_packet, 'TCP',
1209 ignore_zero_checksum=ignore_zero_checksum)
1210
1211 def assert_udp_checksum_valid(self, received_packet,
1212 ignore_zero_checksum=True):
1213 self.assert_checksum_valid(received_packet, 'UDP',
1214 ignore_zero_checksum=ignore_zero_checksum)
1215
1216 def assert_embedded_icmp_checksum_valid(self, received_packet):
1217 if received_packet.haslayer(IPerror):
1218 self.assert_checksum_valid(received_packet, 'IPerror')
1219 if received_packet.haslayer(TCPerror):
1220 self.assert_checksum_valid(received_packet, 'TCPerror')
1221 if received_packet.haslayer(UDPerror):
1222 self.assert_checksum_valid(received_packet, 'UDPerror',
1223 ignore_zero_checksum=True)
1224 if received_packet.haslayer(ICMPerror):
1225 self.assert_checksum_valid(received_packet, 'ICMPerror')
1226
1227 def assert_icmp_checksum_valid(self, received_packet):
1228 self.assert_checksum_valid(received_packet, 'ICMP')
1229 self.assert_embedded_icmp_checksum_valid(received_packet)
1230
1231 def assert_icmpv6_checksum_valid(self, pkt):
1232 if pkt.haslayer(ICMPv6DestUnreach):
1233 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1234 self.assert_embedded_icmp_checksum_valid(pkt)
1235 if pkt.haslayer(ICMPv6EchoRequest):
1236 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1237 if pkt.haslayer(ICMPv6EchoReply):
1238 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1239
Klement Sekera3a343d42019-05-16 14:35:46 +02001240 def get_packet_counter(self, counter):
1241 if counter.startswith("/"):
1242 counter_value = self.statistics.get_counter(counter)
1243 else:
1244 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001245 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001246 for i in range(1, len(counters) - 1):
1247 results = counters[i].split()
1248 if results[1] == counter:
1249 counter_value = int(results[0])
1250 break
1251 return counter_value
1252
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001253 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001254 counter_value = self.get_packet_counter(counter)
1255 self.assert_equal(counter_value, expected_value,
1256 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001257
Ole Troan233e4682019-05-16 15:01:34 +02001258 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001259 counter_value = self.statistics[counter].sum()
Ole Troan233e4682019-05-16 15:01:34 +02001260 self.assert_equal(counter_value, expected_value,
1261 "error counter `%s'" % counter)
1262
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001263 @classmethod
1264 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001265
1266 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1267 # * by Guido, only the main thread can be interrupted.
1268 # */
1269 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1270 if timeout == 0:
1271 # yield quantum
1272 if hasattr(os, 'sched_yield'):
1273 os.sched_yield()
1274 else:
1275 time.sleep(0)
1276 return
1277
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001278 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001279 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001280 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001281 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001282 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001283 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001284 "slept for %es instead of ~%es!",
1285 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001286
1287 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001288 "Finished sleep (%s) - slept %es (wanted %es)",
1289 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001290
Benoît Ganne8c45e512021-02-19 16:39:13 +01001291 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001292 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001293 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001294 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001295
1296 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1297 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001298 if not timeout:
1299 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001300 for i in self.pg_interfaces:
1301 i.get_capture(0, timeout=timeout)
1302 i.assert_nothing_captured(remark=remark)
1303 timeout = 0.1
1304
Benoît Ganne8c45e512021-02-19 16:39:13 +01001305 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1306 trace=True):
Neale Rannsd7603d92019-03-28 08:56:10 +00001307 if not n_rx:
1308 n_rx = len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001309 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001310 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001311 return rx
1312
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001313 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1314 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001315 rx = output.get_capture(len(pkts))
1316 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001317 if not timeout:
1318 timeout = 1
1319 for i in self.pg_interfaces:
1320 if i not in outputs:
1321 i.get_capture(0, timeout=timeout)
1322 i.assert_nothing_captured()
1323 timeout = 0.1
1324
Neale Ranns52fae862018-01-08 04:41:42 -08001325 return rx
1326
Damjan Marionf56b77a2016-10-03 19:44:57 +02001327
juraj.linkes184870a2018-07-16 14:22:01 +02001328def get_testcase_doc_name(test):
1329 return getdoc(test.__class__).splitlines()[0]
1330
1331
Ole Trøan5ba91592018-11-22 10:01:09 +00001332def get_test_description(descriptions, test):
1333 short_description = test.shortDescription()
1334 if descriptions and short_description:
1335 return short_description
1336 else:
1337 return str(test)
1338
1339
juraj.linkes40dd73b2018-09-21 13:55:16 +02001340class TestCaseInfo(object):
1341 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1342 self.logger = logger
1343 self.tempdir = tempdir
1344 self.vpp_pid = vpp_pid
1345 self.vpp_bin_path = vpp_bin_path
1346 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001347
1348
Damjan Marionf56b77a2016-10-03 19:44:57 +02001349class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001350 """
1351 @property result_string
1352 String variable to store the test case result string.
1353 @property errors
1354 List variable containing 2-tuples of TestCase instances and strings
1355 holding formatted tracebacks. Each tuple represents a test which
1356 raised an unexpected exception.
1357 @property failures
1358 List variable containing 2-tuples of TestCase instances and strings
1359 holding formatted tracebacks. Each tuple represents a test where
1360 a failure was explicitly signalled using the TestCase.assert*()
1361 methods.
1362 """
1363
juraj.linkes40dd73b2018-09-21 13:55:16 +02001364 failed_test_cases_info = set()
1365 core_crash_test_cases_info = set()
1366 current_test_case_info = None
1367
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001368 def __init__(self, stream=None, descriptions=None, verbosity=None,
1369 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001370 """
Klement Sekerada505f62017-01-04 12:58:53 +01001371 :param stream File descriptor to store where to report test results.
1372 Set to the standard error stream by default.
1373 :param descriptions Boolean variable to store information if to use
1374 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001375 :param verbosity Integer variable to store required verbosity level.
1376 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001377 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001378 self.stream = stream
1379 self.descriptions = descriptions
1380 self.verbosity = verbosity
1381 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001382 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001383 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001384
Damjan Marionf56b77a2016-10-03 19:44:57 +02001385 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001386 """
1387 Record a test succeeded result
1388
1389 :param test:
1390
1391 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001392 if self.current_test_case_info:
1393 self.current_test_case_info.logger.debug(
1394 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1395 test._testMethodName,
1396 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001397 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001398 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001399
juraj.linkescae64f82018-09-19 15:01:47 +02001400 self.send_result_through_pipe(test, PASS)
1401
Klement Sekeraf62ae122016-10-11 11:47:09 +02001402 def addSkip(self, test, reason):
1403 """
1404 Record a test skipped.
1405
1406 :param test:
1407 :param reason:
1408
1409 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001410 if self.current_test_case_info:
1411 self.current_test_case_info.logger.debug(
1412 "--- addSkip() %s.%s(%s) called, reason is %s" %
1413 (test.__class__.__name__, test._testMethodName,
1414 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001415 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001416 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001417
Klement Sekera558ceab2021-04-08 19:37:41 +02001418 if reason == "not enough cpus":
1419 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1420 else:
1421 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001422
juraj.linkes40dd73b2018-09-21 13:55:16 +02001423 def symlink_failed(self):
1424 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001425 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001426 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001427 link_path = os.path.join(
1428 failed_dir,
1429 '%s-FAILED' %
1430 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001431
1432 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001433 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001434 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001435 "os.symlink(%s, %s)" %
1436 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001437 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001438 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001439 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001440 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001441 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001442
Klement Sekeraf413bef2017-08-15 07:09:02 +02001443 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001444 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001445
juraj.linkescae64f82018-09-19 15:01:47 +02001446 def send_result_through_pipe(self, test, result):
1447 if hasattr(self, 'test_framework_result_pipe'):
1448 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001449 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001450 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001451
juraj.linkes40dd73b2018-09-21 13:55:16 +02001452 def log_error(self, test, err, fn_name):
1453 if self.current_test_case_info:
1454 if isinstance(test, unittest.suite._ErrorHolder):
1455 test_name = test.description
1456 else:
1457 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1458 test._testMethodName,
1459 test._testMethodDoc)
1460 self.current_test_case_info.logger.debug(
1461 "--- %s() %s called, err is %s" %
1462 (fn_name, test_name, err))
1463 self.current_test_case_info.logger.debug(
1464 "formatted exception is:\n%s" %
1465 "".join(format_exception(*err)))
1466
1467 def add_error(self, test, err, unittest_fn, error_type):
1468 if error_type == FAIL:
1469 self.log_error(test, err, 'addFailure')
1470 error_type_str = colorize("FAIL", RED)
1471 elif error_type == ERROR:
1472 self.log_error(test, err, 'addError')
1473 error_type_str = colorize("ERROR", RED)
1474 else:
1475 raise Exception('Error type %s cannot be used to record an '
1476 'error or a failure' % error_type)
1477
1478 unittest_fn(self, test, err)
1479 if self.current_test_case_info:
1480 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1481 (error_type_str,
1482 self.current_test_case_info.tempdir)
1483 self.symlink_failed()
1484 self.failed_test_cases_info.add(self.current_test_case_info)
1485 if is_core_present(self.current_test_case_info.tempdir):
1486 if not self.current_test_case_info.core_crash_test:
1487 if isinstance(test, unittest.suite._ErrorHolder):
1488 test_name = str(test)
1489 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001490 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001491 get_testcase_doc_name(test), test.id())
1492 self.current_test_case_info.core_crash_test = test_name
1493 self.core_crash_test_cases_info.add(
1494 self.current_test_case_info)
1495 else:
1496 self.result_string = '%s [no temp dir]' % error_type_str
1497
1498 self.send_result_through_pipe(test, error_type)
1499
Damjan Marionf56b77a2016-10-03 19:44:57 +02001500 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001501 """
1502 Record a test failed result
1503
1504 :param test:
1505 :param err: error message
1506
1507 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001508 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001509
Damjan Marionf56b77a2016-10-03 19:44:57 +02001510 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001511 """
1512 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001513
Klement Sekeraf62ae122016-10-11 11:47:09 +02001514 :param test:
1515 :param err: error message
1516
1517 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001518 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001519
Damjan Marionf56b77a2016-10-03 19:44:57 +02001520 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001521 """
1522 Get test description
1523
1524 :param test:
1525 :returns: test description
1526
1527 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001528 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001529
Damjan Marionf56b77a2016-10-03 19:44:57 +02001530 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001531 """
1532 Start a test
1533
1534 :param test:
1535
1536 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001537
1538 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001539 if test.__class__ in self.printed:
1540 return
1541
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001542 test_doc = getdoc(test)
1543 if not test_doc:
1544 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001545
Klement Sekeraea6236b2021-03-25 14:03:44 +01001546 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001547 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001548 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001549 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001550
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001551 # This block may overwrite the colorized title above,
1552 # but we want this to stand out and be fixed
1553 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekera558ceab2021-04-08 19:37:41 +02001554 test_title = colorize(
1555 f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001556
Klement Sekeraea6236b2021-03-25 14:03:44 +01001557 if hasattr(test, 'vpp_worker_count'):
1558 if test.vpp_worker_count == 0:
1559 test_title += " [main thread only]"
1560 elif test.vpp_worker_count == 1:
1561 test_title += " [1 worker thread]"
1562 else:
1563 test_title += f" [{test.vpp_worker_count} worker threads]"
1564
Klement Sekera558ceab2021-04-08 19:37:41 +02001565 if test.__class__.skipped_due_to_cpu_lack:
1566 test_title = colorize(
1567 f"{test_title} [skipped - not enough cpus, "
1568 f"required={test.__class__.get_cpus_required()}, "
1569 f"available={max_vpp_cpus}]", YELLOW)
1570
1571 print(double_line_delim)
1572 print(test_title)
1573 print(double_line_delim)
1574 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001575
1576 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001577 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001578 unittest.TestResult.startTest(self, test)
1579 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001580 self.stream.writeln(
1581 "Starting " + self.getDescription(test) + " ...")
1582 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001583
Damjan Marionf56b77a2016-10-03 19:44:57 +02001584 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001585 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001586 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001587
1588 :param test:
1589
1590 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001591 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001592
Damjan Marionf56b77a2016-10-03 19:44:57 +02001593 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001594 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001595 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001596 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001597 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001598 else:
Ole Troan0c629322019-11-28 14:48:44 +01001599 self.stream.writeln("%-68s %4.2f %s" %
1600 (self.getDescription(test),
1601 time.time() - self.start_test,
1602 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001603
1604 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001605
Damjan Marionf56b77a2016-10-03 19:44:57 +02001606 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001607 """
1608 Print errors from running the test case
1609 """
juraj.linkesabec0122018-11-16 17:28:56 +01001610 if len(self.errors) > 0 or len(self.failures) > 0:
1611 self.stream.writeln()
1612 self.printErrorList('ERROR', self.errors)
1613 self.printErrorList('FAIL', self.failures)
1614
1615 # ^^ that is the last output from unittest before summary
1616 if not self.runner.print_summary:
1617 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1618 self.stream = devnull
1619 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001620
Damjan Marionf56b77a2016-10-03 19:44:57 +02001621 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001622 """
1623 Print error list to the output stream together with error type
1624 and test case description.
1625
1626 :param flavour: error type
1627 :param errors: iterable errors
1628
1629 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001630 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001631 self.stream.writeln(double_line_delim)
1632 self.stream.writeln("%s: %s" %
1633 (flavour, self.getDescription(test)))
1634 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001635 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001636
1637
Damjan Marionf56b77a2016-10-03 19:44:57 +02001638class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001639 """
Klement Sekera104543f2017-02-03 07:29:43 +01001640 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001641 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001642
Klement Sekeraf62ae122016-10-11 11:47:09 +02001643 @property
1644 def resultclass(self):
1645 """Class maintaining the results of the tests"""
1646 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001647
juraj.linkes184870a2018-07-16 14:22:01 +02001648 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001649 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001650 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001651 # ignore stream setting here, use hard-coded stdout to be in sync
1652 # with prints from VppTestCase methods ...
1653 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1654 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001655 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001656 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001657
juraj.linkesabec0122018-11-16 17:28:56 +01001658 self.orig_stream = self.stream
1659 self.resultclass.test_framework_result_pipe = result_pipe
1660
1661 self.print_summary = print_summary
1662
1663 def _makeResult(self):
1664 return self.resultclass(self.stream,
1665 self.descriptions,
1666 self.verbosity,
1667 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001668
Damjan Marionf56b77a2016-10-03 19:44:57 +02001669 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001670 """
1671 Run the tests
1672
1673 :param test:
1674
1675 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001676 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001677
1678 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001679 if not self.print_summary:
1680 self.stream = self.orig_stream
1681 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001682 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001683
1684
1685class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001686 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1687 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001688 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001689 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001690 if hasattr(self, 'testcase') and self.testcase.debug_all:
1691 if self.testcase.debug_gdbserver:
1692 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1693 .format(port=self.testcase.gdbserver_port)] + args
1694 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1695 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001696 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001697 self.app_name = os.path.basename(self.app_bin)
1698 if hasattr(self, 'role'):
1699 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001700 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001701 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001702 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001703 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001704
Dave Wallace24564332019-10-21 02:53:14 +00001705 def wait_for_enter(self):
1706 if not hasattr(self, 'testcase'):
1707 return
1708 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1709 print()
1710 print(double_line_delim)
1711 print("Spawned GDB Server for '{app}' with PID: {pid}"
1712 .format(app=self.app_name, pid=self.process.pid))
1713 elif self.testcase.debug_all and self.testcase.debug_gdb:
1714 print()
1715 print(double_line_delim)
1716 print("Spawned '{app}' with PID: {pid}"
1717 .format(app=self.app_name, pid=self.process.pid))
1718 else:
1719 return
1720 print(single_line_delim)
1721 print("You can debug '{app}' using:".format(app=self.app_name))
1722 if self.testcase.debug_gdbserver:
1723 print("sudo gdb " + self.app_bin +
1724 " -ex 'target remote localhost:{port}'"
1725 .format(port=self.testcase.gdbserver_port))
1726 print("Now is the time to attach gdb by running the above "
1727 "command, set up breakpoints etc., then resume from "
1728 "within gdb by issuing the 'continue' command")
1729 self.testcase.gdbserver_port += 1
1730 elif self.testcase.debug_gdb:
1731 print("sudo gdb " + self.app_bin +
1732 " -ex 'attach {pid}'".format(pid=self.process.pid))
1733 print("Now is the time to attach gdb by running the above "
1734 "command and set up breakpoints etc., then resume from"
1735 " within gdb by issuing the 'continue' command")
1736 print(single_line_delim)
1737 input("Press ENTER to continue running the testcase...")
1738
Neale Ranns812ed392017-10-16 04:20:13 -07001739 def run(self):
1740 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001741 if not os.path.exists(executable) or not os.access(
1742 executable, os.F_OK | os.X_OK):
1743 # Exit code that means some system file did not exist,
1744 # could not be opened, or had some other kind of error.
1745 self.result = os.EX_OSFILE
1746 raise EnvironmentError(
1747 "executable '%s' is not found or executable." % executable)
Dave Wallace4ee17d82021-05-20 14:01:51 -04001748 self.logger.debug("Running executable '{app}': '{cmd}'"
1749 .format(app=self.app_name,
1750 cmd=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001751 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001752 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001753 env["CK_LOG_FILE_NAME"] = "-"
1754 self.process = subprocess.Popen(
Dave Wallaceae8d3322021-05-18 17:12:16 -04001755 ['stdbuf', '-o0', '-e0'] + self.args, shell=False, env=env,
1756 preexec_fn=os.setpgrp, stdout=subprocess.PIPE,
1757 stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001758 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001759 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001760 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001761 self.logger.info("Return code is `%s'" % self.process.returncode)
1762 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001763 self.logger.info("Executable `{app}' wrote to stdout:"
1764 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001765 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001766 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001767 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001768 self.logger.info("Executable `{app}' wrote to stderr:"
1769 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001770 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001771 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001772 self.logger.info(single_line_delim)
1773 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001774
Klement Sekera6aa58b72019-05-16 14:34:55 +02001775
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001776if __name__ == '__main__':
1777 pass