blob: 257c85e9f5e766bf659d68654492f2c56215e912 [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
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05004import logging
Paul Vinciguerra72f00042018-11-25 11:05:13 -08005import sys
Ole Trøan162989e2018-11-26 10:27:50 +00006import os
7import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04008import signal
Paul Vinciguerrad6f22172020-12-05 22:39:14 +00009import subprocess
Ole Trøan162989e2018-11-26 10:27:50 +000010import unittest
Klement Sekerab23ffd72021-05-31 16:08:53 +020011import re
Klement Sekera277b89c2016-10-28 13:20:27 +020012import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080013import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000014import random
15import copy
juraj.linkes68ebc832018-11-29 09:37:08 +010016import platform
Klement Sekerab23ffd72021-05-31 16:08:53 +020017import shutil
Ole Trøan162989e2018-11-26 10:27:50 +000018from collections import deque
19from threading import Thread, Event
20from inspect import getdoc, isclass
21from traceback import format_exception
22from logging import FileHandler, DEBUG, Formatter
Andrew Yourtchenko06f32812021-01-14 10:19:08 +000023from enum import Enum
Klement Sekera558ceab2021-04-08 19:37:41 +020024from abc import ABC, abstractmethod
Ray Kinsellab8165b92021-09-22 11:24:06 +010025from struct import pack, unpack
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070026
27import scapy.compat
Klement Sekera56c492a2022-01-10 21:57:27 +000028from scapy.packet import Raw, Packet
Klement Sekerab23ffd72021-05-31 16:08:53 +020029from config import config, available_cpus, num_cpus, max_vpp_cpus
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040030import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080031from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010032from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000033from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070034from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000035from vpp_papi_provider import VppPapiProvider
Neale Ranns6197cb72021-06-03 14:43:21 +000036from vpp_papi import VppEnum
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050037import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000038from vpp_papi.vpp_stats import VPPStats
Ole Troan4376ab22021-03-03 10:40:05 +010039from vpp_papi.vpp_transport_socket import VppTransportSocketIOError
Ole Trøan162989e2018-11-26 10:27:50 +000040from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
41 get_logger, colorize
42from vpp_object import VppObjectRegistry
43from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020044from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
45from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
46from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080047
Klement Sekera558ceab2021-04-08 19:37:41 +020048
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
Klement Sekerab23ffd72021-05-31 16:08:53 +020063if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010064 import debug_internal
65
Klement Sekeraf62ae122016-10-11 11:47:09 +020066"""
67 Test framework module.
68
69 The module provides a set of tools for constructing and running tests and
70 representing the results.
71"""
72
Klement Sekeraf62ae122016-10-11 11:47:09 +020073
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040074class VppDiedError(Exception):
75 """ exception for reporting that the subprocess has died."""
76
77 signals_by_value = {v: k for k, v in signal.__dict__.items() if
78 k.startswith('SIG') and not k.startswith('SIG_')}
79
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040080 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040081 self.rv = rv
82 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040083 self.testcase = testcase
84 self.method_name = method_name
85
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040086 try:
87 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -040088 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040089 pass
90
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040091 if testcase is None and method_name is None:
92 in_msg = ''
93 else:
Klement Sekera79a31db2021-03-12 18:16:10 +010094 in_msg = ' while running %s.%s' % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040095
Klement Sekera79a31db2021-03-12 18:16:10 +010096 if self.rv:
97 msg = "VPP subprocess died unexpectedly%s with return code: %d%s."\
98 % (in_msg, self.rv, ' [%s]' %
99 (self.signal_name if
100 self.signal_name is not None else ''))
101 else:
102 msg = "VPP subprocess died unexpectedly%s." % in_msg
103
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400104 super(VppDiedError, self).__init__(msg)
105
106
Damjan Marionf56b77a2016-10-03 19:44:57 +0200107class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200108 """Private class to create packet info object.
109
110 Help process information about the next packet.
111 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200112 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100113 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200114 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100115 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200116 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100117 #: Store the index of the destination packet generator interface
118 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200119 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100120 #: Store expected ip version
121 ip = -1
122 #: Store expected upper protocol
123 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100124 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200125 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200126
Matej Klotton16a14cd2016-12-07 15:09:13 +0100127 def __eq__(self, other):
128 index = self.index == other.index
129 src = self.src == other.src
130 dst = self.dst == other.dst
131 data = self.data == other.data
132 return index and src and dst and data
133
Klement Sekeraf62ae122016-10-11 11:47:09 +0200134
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100135def pump_output(testclass):
136 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100137 stdout_fragment = ""
138 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400139 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100140 readable = select.select([testclass.vpp.stdout.fileno(),
141 testclass.vpp.stderr.fileno(),
142 testclass.pump_thread_wakeup_pipe[0]],
143 [], [])[0]
144 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100145 read = os.read(testclass.vpp.stdout.fileno(), 102400)
146 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200147 split = read.decode('ascii',
148 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100149 if len(stdout_fragment) > 0:
150 split[0] = "%s%s" % (stdout_fragment, split[0])
151 if len(split) > 0 and split[-1].endswith("\n"):
152 limit = None
153 else:
154 limit = -1
155 stdout_fragment = split[-1]
156 testclass.vpp_stdout_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200157 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100158 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100159 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100160 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100161 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100162 read = os.read(testclass.vpp.stderr.fileno(), 102400)
163 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200164 split = read.decode('ascii',
165 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100166 if len(stderr_fragment) > 0:
167 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200168 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100169 limit = None
170 else:
171 limit = -1
172 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200173
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100174 testclass.vpp_stderr_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200175 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100176 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100177 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100178 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800179 # ignoring the dummy pipe here intentionally - the
180 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200181
182
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800183def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100184 return platform.machine() == 'aarch64'
185
Klement Sekera6aa58b72019-05-16 14:34:55 +0200186
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800187is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100188
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800189
Klement Sekera909a6a12017-08-08 04:33:53 +0200190class KeepAliveReporter(object):
191 """
192 Singleton object which reports test start to parent process
193 """
194 _shared_state = {}
195
196 def __init__(self):
197 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800198 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200199
200 @property
201 def pipe(self):
202 return self._pipe
203
204 @pipe.setter
205 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800206 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200207 raise Exception("Internal error - pipe should only be set once.")
208 self._pipe = pipe
209
juraj.linkes40dd73b2018-09-21 13:55:16 +0200210 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200211 """
212 Write current test tmpdir & desc to keep-alive pipe to signal liveness
213 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200214 if self.pipe is None:
215 # if not running forked..
216 return
217
Klement Sekera909a6a12017-08-08 04:33:53 +0200218 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200219 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200220 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200221 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200222
Klement Sekerab23ffd72021-05-31 16:08:53 +0200223 self.pipe.send((desc, config.vpp, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200224
225
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000226class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000227 # marks the suites that must run at the end
228 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000229 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000230 # marks the suites broken on VPP multi-worker
231 FIXME_VPP_WORKERS = 2
Naveen Joy6eaeea92021-09-09 17:57:02 -0700232 # marks the suites broken when ASan is enabled
233 FIXME_ASAN = 3
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000234
235
236def create_tag_decorator(e):
237 def decorator(cls):
238 try:
239 cls.test_tags.append(e)
240 except AttributeError:
241 cls.test_tags = [e]
242 return cls
243 return decorator
244
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000245
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000246tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000247tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Naveen Joy6eaeea92021-09-09 17:57:02 -0700248tag_fixme_asan = create_tag_decorator(TestCaseTag.FIXME_ASAN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000249
250
Klement Sekerae2636852021-03-16 12:52:12 +0100251class DummyVpp:
252 returncode = None
253 pid = 0xcafebafe
254
255 def poll(self):
256 pass
257
258 def terminate(self):
259 pass
260
261
Klement Sekera558ceab2021-04-08 19:37:41 +0200262class CPUInterface(ABC):
263 cpus = []
264 skipped_due_to_cpu_lack = False
265
266 @classmethod
267 @abstractmethod
268 def get_cpus_required(cls):
269 pass
270
271 @classmethod
272 def assign_cpus(cls, cpus):
273 cls.cpus = cpus
274
275
276class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100277 """This subclass is a base class for VPP test cases that are implemented as
278 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200279 """
280
Arthur de Kerhordb023802021-03-11 10:26:54 -0800281 extra_vpp_statseg_config = ""
Ole Troana45dc072018-12-21 16:04:22 +0100282 extra_vpp_punt_config = []
283 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500284 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400285 vapi_response_timeout = 5
Klement Sekera140af152022-02-18 10:30:51 +0000286 remove_configured_vpp_objects_on_tear_down = True
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100287
Klement Sekeraf62ae122016-10-11 11:47:09 +0200288 @property
289 def packet_infos(self):
290 """List of packet infos"""
291 return self._packet_infos
292
Klement Sekeradab231a2016-12-21 08:50:14 +0100293 @classmethod
294 def get_packet_count_for_if_idx(cls, dst_if_index):
295 """Get the number of packet info for specified destination if index"""
296 if dst_if_index in cls._packet_count_for_dst_if_idx:
297 return cls._packet_count_for_dst_if_idx[dst_if_index]
298 else:
299 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200300
301 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000302 def has_tag(cls, tag):
303 """ if the test case has a given tag - return true """
304 try:
305 return tag in cls.test_tags
306 except AttributeError:
307 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000308 return False
309
310 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000311 def is_tagged_run_solo(cls):
312 """ if the test case class is timing-sensitive - return true """
313 return cls.has_tag(TestCaseTag.RUN_SOLO)
314
315 @classmethod
Naveen Joy6eaeea92021-09-09 17:57:02 -0700316 def skip_fixme_asan(cls):
317 """ if @tag_fixme_asan & ASan is enabled - mark for skip """
318 if cls.has_tag(TestCaseTag.FIXME_ASAN):
319 vpp_extra_cmake_args = os.environ.get('VPP_EXTRA_CMAKE_ARGS', '')
320 if 'DVPP_ENABLE_SANITIZE_ADDR=ON' in vpp_extra_cmake_args:
321 cls = unittest.skip("Skipping @tag_fixme_asan tests")(cls)
322
323 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200324 def instance(cls):
325 """Return the instance of this testcase"""
326 return cls.test_instance
327
Damjan Marionf56b77a2016-10-03 19:44:57 +0200328 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200329 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000330 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200331 cls.debug_core = False
332 cls.debug_gdb = False
333 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000334 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100335 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200336 if d is None:
337 return
338 dl = d.lower()
339 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200340 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000341 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200342 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000343 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200344 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100345 elif dl == "attach":
346 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200347 else:
348 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000349 if dl == "gdb-all" or dl == "gdbserver-all":
350 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200351
Klement Sekera558ceab2021-04-08 19:37:41 +0200352 @classmethod
353 def get_vpp_worker_count(cls):
354 if not hasattr(cls, "vpp_worker_count"):
355 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
356 cls.vpp_worker_count = 0
357 else:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200358 cls.vpp_worker_count = config.vpp_worker_count
Klement Sekera558ceab2021-04-08 19:37:41 +0200359 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200360
Klement Sekera558ceab2021-04-08 19:37:41 +0200361 @classmethod
362 def get_cpus_required(cls):
363 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200364
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800365 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200366 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200367 """ Set-up the test case class based on environment variables """
Klement Sekerab23ffd72021-05-31 16:08:53 +0200368 cls.step = config.step
369 cls.plugin_path = ":".join(config.vpp_plugin_dir)
370 cls.test_plugin_path = ":".join(config.vpp_test_plugin_dir)
371 cls.extern_plugin_path = ":".join(config.extern_plugin_dir)
Ole Troana45dc072018-12-21 16:04:22 +0100372 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100373 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100374 debug_cli = "cli-listen localhost:5002"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200375 size = re.search(r"\d+[gG]", config.coredump_size)
376 if size:
377 coredump_size = f"coredump-size {config.coredump_size}".lower()
378 else:
Ole Troana45dc072018-12-21 16:04:22 +0100379 coredump_size = "coredump-size unlimited"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200380 default_variant = config.variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000381 if default_variant is not None:
382 default_variant = "defaults { %s 100 }" % default_variant
383 else:
384 default_variant = ""
385
Klement Sekerab23ffd72021-05-31 16:08:53 +0200386 api_fuzzing = config.api_fuzz
Dave Barach77841402020-04-29 17:04:10 -0400387 if api_fuzzing is None:
388 api_fuzzing = 'off'
389
Klement Sekera8d815022021-03-15 16:58:10 +0100390 cls.vpp_cmdline = [
Klement Sekerab23ffd72021-05-31 16:08:53 +0200391 config.vpp,
Klement Sekera8d815022021-03-15 16:58:10 +0100392 "unix", "{", "nodaemon", debug_cli, "full-coredump",
393 coredump_size, "runtime-dir", cls.tempdir, "}",
394 "api-trace", "{", "on", "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100395 "api-segment", "{", "prefix", cls.get_api_segment_prefix(), "}",
Klement Sekera558ceab2021-04-08 19:37:41 +0200396 "cpu", "{", "main-core", str(cls.cpus[0]), ]
Klement Sekerab23ffd72021-05-31 16:08:53 +0200397 if cls.extern_plugin_path not in (None, ""):
Damjan Marion5546e432021-09-30 20:04:14 +0200398 cls.extra_vpp_plugin_config.append(
Klement Sekerab23ffd72021-05-31 16:08:53 +0200399 "add-path %s" % cls.extern_plugin_path)
Klement Sekera558ceab2021-04-08 19:37:41 +0200400 if cls.get_vpp_worker_count():
401 cls.vpp_cmdline.extend([
402 "corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])])
Klement Sekera8d815022021-03-15 16:58:10 +0100403 cls.vpp_cmdline.extend([
404 "}",
405 "physmem", "{", "max-size", "32m", "}",
Arthur de Kerhordb023802021-03-11 10:26:54 -0800406 "statseg", "{", "socket-name", cls.get_stats_sock_path(),
407 cls.extra_vpp_statseg_config, "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100408 "socksvr", "{", "socket-name", cls.get_api_sock_path(), "}",
Klement Sekera8d815022021-03-15 16:58:10 +0100409 "node { ", default_variant, "}",
410 "api-fuzz {", api_fuzzing, "}",
411 "plugins", "{", "plugin", "dpdk_plugin.so", "{", "disable", "}",
412 "plugin", "rdma_plugin.so", "{", "disable", "}",
413 "plugin", "lisp_unittest_plugin.so", "{", "enable", "}",
414 "plugin", "unittest_plugin.so", "{", "enable", "}"
415 ] + cls.extra_vpp_plugin_config + ["}", ])
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000416
Ole Troana45dc072018-12-21 16:04:22 +0100417 if cls.extra_vpp_punt_config is not None:
418 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Dave Barach7d31ab22019-05-08 19:18:18 -0400419
Klement Sekerae2636852021-03-16 12:52:12 +0100420 if not cls.debug_attach:
421 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
422 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200423
424 @classmethod
425 def wait_for_enter(cls):
426 if cls.debug_gdbserver:
427 print(double_line_delim)
428 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
429 elif cls.debug_gdb:
430 print(double_line_delim)
431 print("Spawned VPP with PID: %d" % cls.vpp.pid)
432 else:
433 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
434 return
435 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000436 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200437 if cls.debug_gdbserver:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200438 print(f"sudo gdb {config.vpp} "
439 f"-ex 'target remote localhost:{cls.gdbserver_port}'")
Dave Wallace24564332019-10-21 02:53:14 +0000440 print("Now is the time to attach gdb by running the above "
441 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200442 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000443 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200444 elif cls.debug_gdb:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200445 print(f"sudo gdb {config.vpp} -ex 'attach {cls.vpp.pid}'")
Dave Wallace24564332019-10-21 02:53:14 +0000446 print("Now is the time to attach gdb by running the above "
447 "command and set up breakpoints etc., then resume VPP from"
448 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200449 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800450 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200451
452 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100453 def attach_vpp(cls):
454 cls.vpp = DummyVpp()
455
456 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200457 def run_vpp(cls):
Klement Sekera558ceab2021-04-08 19:37:41 +0200458 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200459 cmdline = cls.vpp_cmdline
460
461 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100462 gdbserver = '/usr/bin/gdbserver'
Klement Sekera558ceab2021-04-08 19:37:41 +0200463 if not os.path.isfile(gdbserver) or\
Klement Sekera931be3a2016-11-03 05:36:01 +0100464 not os.access(gdbserver, os.X_OK):
465 raise Exception("gdbserver binary '%s' does not exist or is "
466 "not executable" % gdbserver)
467
Dave Wallace24564332019-10-21 02:53:14 +0000468 cmdline = [gdbserver, 'localhost:{port}'
469 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200470 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
471
Klement Sekera931be3a2016-11-03 05:36:01 +0100472 try:
473 cls.vpp = subprocess.Popen(cmdline,
474 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100475 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800476 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800477 cls.logger.critical("Subprocess returned with non-0 return code: ("
478 "%s)", e.returncode)
479 raise
480 except OSError as e:
481 cls.logger.critical("Subprocess returned with OS error: "
482 "(%s) %s", e.errno, e.strerror)
483 raise
484 except Exception as e:
485 cls.logger.exception("Subprocess returned unexpected from "
486 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100487 raise
488
Klement Sekera277b89c2016-10-28 13:20:27 +0200489 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100490
Damjan Marionf56b77a2016-10-03 19:44:57 +0200491 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000492 def wait_for_coredump(cls):
493 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400494 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000495 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400496 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000497 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400498 ok = False
499 while time.time() < deadline:
500 cls.sleep(1)
501 size = curr_size
502 curr_size = os.path.getsize(corefile)
503 if size == curr_size:
504 ok = True
505 break
506 if not ok:
507 cls.logger.error("Timed out waiting for coredump to complete:"
508 " %s", corefile)
509 else:
510 cls.logger.error("Coredump complete: %s, size %d",
511 corefile, curr_size)
512
513 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100514 def get_stats_sock_path(cls):
515 return "%s/stats.sock" % cls.tempdir
516
517 @classmethod
518 def get_api_sock_path(cls):
519 return "%s/api.sock" % cls.tempdir
520
521 @classmethod
522 def get_api_segment_prefix(cls):
523 return os.path.basename(cls.tempdir) # Only used for VAPI
524
525 @classmethod
526 def get_tempdir(cls):
Klement Sekerab23ffd72021-05-31 16:08:53 +0200527 tmpdir = f"{config.tmp_dir}/vpp-unittest-{cls.__name__}"
528 if config.wipe_tmp_dir:
529 shutil.rmtree(tmpdir, ignore_errors=True)
530 os.mkdir(tmpdir)
531 return tmpdir
532
533 @classmethod
534 def create_file_handler(cls):
535 if config.log_dir is None:
536 cls.file_handler = FileHandler(f"{cls.tempdir}/log.txt")
537 return
538
539 logdir = f"{config.log_dir}/vpp-unittest-{cls.__name__}"
540 if config.wipe_tmp_dir:
541 shutil.rmtree(logdir, ignore_errors=True)
542 os.mkdir(logdir)
543 cls.file_handler = FileHandler(f"{logdir}/log.txt")
Klement Sekerae2636852021-03-16 12:52:12 +0100544
545 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200546 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200547 """
548 Perform class setup before running the testcase
549 Remove shared memory files, start vpp and connect the vpp-api
550 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800551 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100552 cls.logger = get_logger(cls.__name__)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200553 random.seed(config.rnd_seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100554 if hasattr(cls, 'parallel_handler'):
555 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100556 cls.logger.propagate = False
Klement Sekerab23ffd72021-05-31 16:08:53 +0200557 cls.set_debug_flags(config.debug)
Klement Sekerae2636852021-03-16 12:52:12 +0100558 cls.tempdir = cls.get_tempdir()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200559 cls.create_file_handler()
Klement Sekera027dbd52017-04-11 06:01:53 +0200560 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)
Klement Sekerae2636852021-03-16 12:52:12 +0100565 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200566 os.chdir(cls.tempdir)
Ole Troan4376ab22021-03-03 10:40:05 +0100567 cls.logger.info("Temporary dir is %s, api socket is %s",
Klement Sekerae2636852021-03-16 12:52:12 +0100568 cls.tempdir, cls.get_api_sock_path())
Klement Sekerab23ffd72021-05-31 16:08:53 +0200569 cls.logger.debug("Random seed is %s", config.rnd_seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200570 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100571 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200572 cls._pcaps = []
573 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200574 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100575 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100576 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200577 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200578 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200579 # need to catch exceptions here because if we raise, then the cleanup
580 # doesn't get called and we might end with a zombie vpp
581 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100582 if cls.debug_attach:
583 cls.attach_vpp()
584 else:
585 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200586 cls.reporter.send_keep_alive(cls, 'setUpClass')
587 VppTestResult.current_test_case_info = TestCaseInfo(
Klement Sekerab23ffd72021-05-31 16:08:53 +0200588 cls.logger, cls.tempdir, cls.vpp.pid, config.vpp)
Klement Sekerae4504c62016-12-08 10:16:41 +0100589 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100590 cls.vpp_stderr_deque = deque()
Klement Sekerae2636852021-03-16 12:52:12 +0100591 if not cls.debug_attach:
592 cls.pump_thread_stop_flag = Event()
593 cls.pump_thread_wakeup_pipe = os.pipe()
594 cls.pump_thread = Thread(target=pump_output, args=(cls,))
595 cls.pump_thread.daemon = True
596 cls.pump_thread.start()
597 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400598 cls.vapi_response_timeout = 0
Ole Troan4376ab22021-03-03 10:40:05 +0100599 cls.vapi = VppPapiProvider(cls.__name__, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400600 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100601 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400602 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100603 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400604 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100605 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100606 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200607 try:
608 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100609 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200610 cls.vpp_startup_failed = True
611 cls.logger.critical(
612 "VPP died shortly after startup, check the"
613 " output to standard error for possible cause")
614 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100615 try:
616 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100617 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500618 cls.logger.debug("Exception connecting to vapi: %s" % e)
619 cls.vapi.disconnect()
620
Klement Sekera085f5c02016-11-24 01:59:16 +0100621 if cls.debug_gdbserver:
622 print(colorize("You're running VPP inside gdbserver but "
623 "VPP-API connection failed, did you forget "
624 "to 'continue' VPP from within gdb?", RED))
Ole Troan4376ab22021-03-03 10:40:05 +0100625 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100626 if cls.debug_attach:
627 last_line = cls.vapi.cli("show thread").split("\n")[-2]
628 cls.vpp_worker_count = int(last_line.split(" ")[0])
629 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400630 except vpp_papi.VPPRuntimeError as e:
631 cls.logger.debug("%s" % e)
632 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100633 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000634 except Exception as e:
635 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400636 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100637 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200638
Damjan Marionf56b77a2016-10-03 19:44:57 +0200639 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500640 def _debug_quit(cls):
641 if (cls.debug_gdbserver or cls.debug_gdb):
642 try:
643 cls.vpp.poll()
644
645 if cls.vpp.returncode is None:
646 print()
647 print(double_line_delim)
648 print("VPP or GDB server is still running")
649 print(single_line_delim)
650 input("When done debugging, press ENTER to kill the "
651 "process and finish running the testcase...")
652 except AttributeError:
653 pass
654
655 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200656 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200657 """
658 Disconnect vpp-api, kill vpp and cleanup shared memory files
659 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500660 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200661
juraj.linkes184870a2018-07-16 14:22:01 +0200662 # first signal that we want to stop the pump thread, then wake it up
663 if hasattr(cls, 'pump_thread_stop_flag'):
664 cls.pump_thread_stop_flag.set()
665 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100666 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100667 if hasattr(cls, 'pump_thread'):
668 cls.logger.debug("Waiting for pump thread to stop")
669 cls.pump_thread.join()
670 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500671 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100672 cls.vpp_stderr_reader_thread.join()
673
Klement Sekeraf62ae122016-10-11 11:47:09 +0200674 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100675 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100676 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700677 cls.logger.debug("Disconnecting class vapi client on %s",
678 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100679 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700680 cls.logger.debug("Deleting class vapi attribute on %s",
681 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100682 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200683 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100684 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000685 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100686 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400687 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100688 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100689 try:
690 outs, errs = cls.vpp.communicate(timeout=5)
691 except subprocess.TimeoutExpired:
692 cls.vpp.kill()
693 outs, errs = cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700694 cls.logger.debug("Deleting class vpp attribute on %s",
695 cls.__name__)
Klement Sekerae2636852021-03-16 12:52:12 +0100696 if not cls.debug_attach:
697 cls.vpp.stdout.close()
698 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200699 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200700
Klement Sekera3747c752017-04-10 06:30:17 +0200701 if cls.vpp_startup_failed:
702 stdout_log = cls.logger.info
703 stderr_log = cls.logger.critical
704 else:
705 stdout_log = cls.logger.info
706 stderr_log = cls.logger.info
707
Klement Sekerae4504c62016-12-08 10:16:41 +0100708 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200709 stdout_log(single_line_delim)
710 stdout_log('VPP output to stdout while running %s:', cls.__name__)
711 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100712 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200713 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
714 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200715 stdout_log('\n%s', vpp_output)
716 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200717
Klement Sekerae4504c62016-12-08 10:16:41 +0100718 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200719 stderr_log(single_line_delim)
720 stderr_log('VPP output to stderr while running %s:', cls.__name__)
721 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100722 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200723 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
724 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200725 stderr_log('\n%s', vpp_output)
726 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200727
Damjan Marionf56b77a2016-10-03 19:44:57 +0200728 @classmethod
729 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200730 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700731 cls.logger.debug("--- tearDownClass() for %s called ---" %
732 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200733 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200734 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200735 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100736 cls.reset_packet_infos()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200737 if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +0100738 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200739
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700740 def show_commands_at_teardown(self):
741 """ Allow subclass specific teardown logging additions."""
742 self.logger.info("--- No test specific show commands provided. ---")
743
Damjan Marionf56b77a2016-10-03 19:44:57 +0200744 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200745 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100746 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
747 (self.__class__.__name__, self._testMethodName,
748 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700749
750 try:
751 if not self.vpp_dead:
752 self.logger.debug(self.vapi.cli("show trace max 1000"))
753 self.logger.info(self.vapi.ppcli("show interface"))
754 self.logger.info(self.vapi.ppcli("show hardware"))
755 self.logger.info(self.statistics.set_errors_str())
756 self.logger.info(self.vapi.ppcli("show run"))
757 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400758 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700759 self.logger.info("Logging testcase specific show commands.")
760 self.show_commands_at_teardown()
Klement Sekera140af152022-02-18 10:30:51 +0000761 if self.remove_configured_vpp_objects_on_tear_down:
762 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500763 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000764 m = self._testMethodName
765 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500766 tmp_api_trace = "/tmp/%s" % api_trace
767 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
768 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
769 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
770 vpp_api_trace_log))
771 os.rename(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100772 except VppTransportSocketIOError:
773 self.logger.debug("VppTransportSocketIOError: Vpp dead. "
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700774 "Cannot log show commands.")
775 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100776 else:
777 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200778
Damjan Marionf56b77a2016-10-03 19:44:57 +0200779 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200780 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800781 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200782 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100783 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400784 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
785 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100786 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100787 self.vpp_stdout_deque.append(
788 "--- test setUp() for %s.%s(%s) starts here ---\n" %
789 (self.__class__.__name__, self._testMethodName,
790 self._testMethodDoc))
791 self.vpp_stderr_deque.append(
792 "--- test setUp() for %s.%s(%s) starts here ---\n" %
793 (self.__class__.__name__, self._testMethodName,
794 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200795 self.vapi.cli("clear trace")
796 # store the test instance inside the test class - so that objects
797 # holding the class can access instance methods (like assertEqual)
798 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200799
Damjan Marionf56b77a2016-10-03 19:44:57 +0200800 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200801 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200802 """
803 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200804
Klement Sekera75e7d132017-09-20 08:26:30 +0200805 :param interfaces: iterable interface indexes (if None,
806 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200807
Klement Sekeraf62ae122016-10-11 11:47:09 +0200808 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200809 if interfaces is None:
810 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200811 for i in interfaces:
812 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200813
Damjan Marionf56b77a2016-10-03 19:44:57 +0200814 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200815 def register_pcap(cls, intf, worker):
816 """ Register a pcap in the testclass """
Klement Sekera9225dee2016-12-12 08:36:58 +0100817 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200818 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100819
820 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000821 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400822 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
823 # returns float("2.190522")
824 timestr = cls.vapi.cli('show clock')
825 head, sep, tail = timestr.partition(',')
826 head, sep, tail = head.partition('Time now')
827 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000828
829 @classmethod
830 def sleep_on_vpp_time(cls, sec):
831 """ Sleep according to time in VPP world """
832 # On a busy system with many processes
833 # we might end up with VPP time being slower than real world
834 # So take that into account when waiting for VPP to do something
835 start_time = cls.get_vpp_time()
836 while cls.get_vpp_time() - start_time < sec:
837 cls.sleep(0.1)
838
839 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100840 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000841 """ Enable the PG, wait till it is done, then clean up """
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200842 for (intf, worker) in cls._old_pcaps:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200843 intf.handle_old_pcap_file(intf.get_in_path(worker),
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200844 intf.in_history_counter)
845 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100846 if trace:
847 cls.vapi.cli("clear trace")
848 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200849 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000850 # PG, when starts, runs to completion -
851 # so let's avoid a race condition,
852 # and wait a little till it's done.
853 # Then clean it up - and then be gone.
854 deadline = time.time() + 300
855 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
856 cls.sleep(0.01) # yield
857 if time.time() > deadline:
858 cls.logger.error("Timeout waiting for pg to stop")
859 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200860 for intf, worker in cls._pcaps:
Klement Sekera7ba9fae2021-03-31 13:36:38 +0200861 cls.vapi.cli('packet-generator delete %s' %
862 intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200863 cls._old_pcaps = cls._pcaps
864 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200865
Damjan Marionf56b77a2016-10-03 19:44:57 +0200866 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000867 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0,
868 mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200869 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100870 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200871
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100872 :param interfaces: iterable indexes of the interfaces.
873 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200874
Klement Sekeraf62ae122016-10-11 11:47:09 +0200875 """
876 result = []
877 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +0000878 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200879 setattr(cls, intf.name, intf)
880 result.append(intf)
881 cls.pg_interfaces = result
882 return result
883
Matej Klotton0178d522016-11-04 11:11:44 +0100884 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000885 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
886 pgmode = VppEnum.vl_api_pg_interface_mode_t
887 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
888 pgmode.PG_API_MODE_IP4)
889
890 @classmethod
891 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
892 pgmode = VppEnum.vl_api_pg_interface_mode_t
893 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
894 pgmode.PG_API_MODE_IP6)
895
896 @classmethod
897 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
898 pgmode = VppEnum.vl_api_pg_interface_mode_t
899 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
900 pgmode.PG_API_MODE_ETHERNET)
901
902 @classmethod
903 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
904 pgmode = VppEnum.vl_api_pg_interface_mode_t
905 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
906 pgmode.PG_API_MODE_ETHERNET)
907
908 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200909 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100910 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100911 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100912
Klement Sekerab9ef2732018-06-24 22:49:33 +0200913 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100914 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100915 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200916 result = [VppLoInterface(cls) for i in range(count)]
917 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100918 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100919 cls.lo_interfaces = result
920 return result
921
Neale Ranns192b13f2019-03-15 02:16:20 -0700922 @classmethod
923 def create_bvi_interfaces(cls, count):
924 """
925 Create BVI interfaces.
926
927 :param count: number of interfaces created.
928 :returns: List of created interfaces.
929 """
930 result = [VppBviInterface(cls) for i in range(count)]
931 for intf in result:
932 setattr(cls, intf.name, intf)
933 cls.bvi_interfaces = result
934 return result
935
Damjan Marionf56b77a2016-10-03 19:44:57 +0200936 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200937 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200938 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200939 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200940 NOTE: Currently works only when Raw layer is present.
941
942 :param packet: packet
943 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200944 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200945
946 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200947 packet_len = len(packet) + 4
948 extend = size - packet_len
949 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200950 num = (extend // len(padding)) + 1
951 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200952
Klement Sekeradab231a2016-12-21 08:50:14 +0100953 @classmethod
954 def reset_packet_infos(cls):
955 """ Reset the list of packet info objects and packet counts to zero """
956 cls._packet_infos = {}
957 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200958
Klement Sekeradab231a2016-12-21 08:50:14 +0100959 @classmethod
960 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200961 """
962 Create packet info object containing the source and destination indexes
963 and add it to the testcase's packet info list
964
Klement Sekeradab231a2016-12-21 08:50:14 +0100965 :param VppInterface src_if: source interface
966 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200967
968 :returns: _PacketInfo object
969
970 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200971 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100972 info.index = len(cls._packet_infos)
973 info.src = src_if.sw_if_index
974 info.dst = dst_if.sw_if_index
975 if isinstance(dst_if, VppSubInterface):
976 dst_idx = dst_if.parent.sw_if_index
977 else:
978 dst_idx = dst_if.sw_if_index
979 if dst_idx in cls._packet_count_for_dst_if_idx:
980 cls._packet_count_for_dst_if_idx[dst_idx] += 1
981 else:
982 cls._packet_count_for_dst_if_idx[dst_idx] = 1
983 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200984 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200985
Damjan Marionf56b77a2016-10-03 19:44:57 +0200986 @staticmethod
987 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200988 """
989 Convert _PacketInfo object to packet payload
990
991 :param info: _PacketInfo object
992
993 :returns: string containing serialized data from packet info
994 """
Ray Kinsellab8165b92021-09-22 11:24:06 +0100995
996 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
997 return pack('iiiih', info.index, info.src,
998 info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200999
Damjan Marionf56b77a2016-10-03 19:44:57 +02001000 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001001 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001002 """
1003 Convert packet payload to _PacketInfo object
1004
1005 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001006 :type payload: <class 'scapy.packet.Raw'>
1007 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001008 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001009 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001010 :returns: _PacketInfo object containing de-serialized data from payload
1011
1012 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001013
1014 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1015 payload_b = getattr(payload, payload_field)[:18]
1016
Damjan Marionf56b77a2016-10-03 19:44:57 +02001017 info = _PacketInfo()
Ray Kinsellab8165b92021-09-22 11:24:06 +01001018 info.index, info.src, info.dst, info.ip, info.proto \
1019 = unpack('iiiih', payload_b)
1020
1021 # some SRv6 TCs depend on get an exception if bad values are detected
1022 if info.index > 0x4000:
1023 raise ValueError('Index value is invalid')
1024
Damjan Marionf56b77a2016-10-03 19:44:57 +02001025 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001026
Damjan Marionf56b77a2016-10-03 19:44:57 +02001027 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001028 """
1029 Iterate over the packet info list stored in the testcase
1030 Start iteration with first element if info is None
1031 Continue based on index in info if info is specified
1032
1033 :param info: info or None
1034 :returns: next info in list or None if no more infos
1035 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001036 if info is None:
1037 next_index = 0
1038 else:
1039 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001040 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001041 return None
1042 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001043 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001044
Klement Sekeraf62ae122016-10-11 11:47:09 +02001045 def get_next_packet_info_for_interface(self, src_index, info):
1046 """
1047 Search the packet info list for the next packet info with same source
1048 interface index
1049
1050 :param src_index: source interface index to search for
1051 :param info: packet info - where to start the search
1052 :returns: packet info or None
1053
1054 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001055 while True:
1056 info = self.get_next_packet_info(info)
1057 if info is None:
1058 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001059 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001060 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001061
Klement Sekeraf62ae122016-10-11 11:47:09 +02001062 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1063 """
1064 Search the packet info list for the next packet info with same source
1065 and destination interface indexes
1066
1067 :param src_index: source interface index to search for
1068 :param dst_index: destination interface index to search for
1069 :param info: packet info - where to start the search
1070 :returns: packet info or None
1071
1072 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001073 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001074 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001075 if info is None:
1076 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001077 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001078 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001079
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001080 def assert_equal(self, real_value, expected_value, name_or_class=None):
1081 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001082 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001083 return
1084 try:
1085 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1086 msg = msg % (getdoc(name_or_class).strip(),
1087 real_value, str(name_or_class(real_value)),
1088 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001089 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001090 msg = "Invalid %s: %s does not match expected value %s" % (
1091 name_or_class, real_value, expected_value)
1092
1093 self.assertEqual(real_value, expected_value, msg)
1094
Klement Sekerab17dd962017-01-09 07:43:48 +01001095 def assert_in_range(self,
1096 real_value,
1097 expected_min,
1098 expected_max,
1099 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001100 if name is None:
1101 msg = None
1102 else:
1103 msg = "Invalid %s: %s out of range <%s,%s>" % (
1104 name, real_value, expected_min, expected_max)
1105 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1106
Klement Sekerad81ae412018-05-16 10:52:54 +02001107 def assert_packet_checksums_valid(self, packet,
1108 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001109 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001110 udp_layers = ['UDP', 'UDPerror']
1111 checksum_fields = ['cksum', 'chksum']
1112 checksums = []
1113 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001114 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001115 while True:
1116 layer = temp.getlayer(counter)
1117 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001118 layer = layer.copy()
1119 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001120 for cf in checksum_fields:
1121 if hasattr(layer, cf):
1122 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001123 0 == getattr(layer, cf) and \
1124 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001125 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001126 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001127 checksums.append((counter, cf))
1128 else:
1129 break
1130 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001131 if 0 == len(checksums):
1132 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001133 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001134 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001135 calc_sum = getattr(temp[layer], cf)
1136 self.assert_equal(
1137 getattr(received[layer], cf), calc_sum,
1138 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1139 self.logger.debug(
1140 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1141 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001142
1143 def assert_checksum_valid(self, received_packet, layer,
1144 field_name='chksum',
1145 ignore_zero_checksum=False):
1146 """ Check checksum of received packet on given layer """
1147 received_packet_checksum = getattr(received_packet[layer], field_name)
1148 if ignore_zero_checksum and 0 == received_packet_checksum:
1149 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001150 recalculated = received_packet.__class__(
1151 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001152 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001153 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001154 self.assert_equal(received_packet_checksum,
1155 getattr(recalculated[layer], field_name),
1156 "packet checksum on layer: %s" % layer)
1157
1158 def assert_ip_checksum_valid(self, received_packet,
1159 ignore_zero_checksum=False):
1160 self.assert_checksum_valid(received_packet, 'IP',
1161 ignore_zero_checksum=ignore_zero_checksum)
1162
1163 def assert_tcp_checksum_valid(self, received_packet,
1164 ignore_zero_checksum=False):
1165 self.assert_checksum_valid(received_packet, 'TCP',
1166 ignore_zero_checksum=ignore_zero_checksum)
1167
1168 def assert_udp_checksum_valid(self, received_packet,
1169 ignore_zero_checksum=True):
1170 self.assert_checksum_valid(received_packet, 'UDP',
1171 ignore_zero_checksum=ignore_zero_checksum)
1172
1173 def assert_embedded_icmp_checksum_valid(self, received_packet):
1174 if received_packet.haslayer(IPerror):
1175 self.assert_checksum_valid(received_packet, 'IPerror')
1176 if received_packet.haslayer(TCPerror):
1177 self.assert_checksum_valid(received_packet, 'TCPerror')
1178 if received_packet.haslayer(UDPerror):
1179 self.assert_checksum_valid(received_packet, 'UDPerror',
1180 ignore_zero_checksum=True)
1181 if received_packet.haslayer(ICMPerror):
1182 self.assert_checksum_valid(received_packet, 'ICMPerror')
1183
1184 def assert_icmp_checksum_valid(self, received_packet):
1185 self.assert_checksum_valid(received_packet, 'ICMP')
1186 self.assert_embedded_icmp_checksum_valid(received_packet)
1187
1188 def assert_icmpv6_checksum_valid(self, pkt):
1189 if pkt.haslayer(ICMPv6DestUnreach):
1190 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1191 self.assert_embedded_icmp_checksum_valid(pkt)
1192 if pkt.haslayer(ICMPv6EchoRequest):
1193 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1194 if pkt.haslayer(ICMPv6EchoReply):
1195 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1196
Klement Sekera107ad732022-02-18 10:32:08 +00001197 def get_counter(self, counter):
Klement Sekera3a343d42019-05-16 14:35:46 +02001198 if counter.startswith("/"):
1199 counter_value = self.statistics.get_counter(counter)
1200 else:
1201 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001202 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001203 for i in range(1, len(counters) - 1):
1204 results = counters[i].split()
1205 if results[1] == counter:
1206 counter_value = int(results[0])
1207 break
1208 return counter_value
1209
Klement Sekera107ad732022-02-18 10:32:08 +00001210 def assert_counter_equal(self, counter, expected_value,
1211 thread=None, index=0):
1212 c = self.get_counter(counter)
1213 if thread is not None:
1214 c = c[thread][index]
1215 else:
1216 c = sum(x[index] for x in c)
1217 self.assert_equal(c, expected_value, "counter `%s'" % counter)
1218
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001219 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera107ad732022-02-18 10:32:08 +00001220 counter_value = self.get_counter(counter)
Klement Sekera6aa58b72019-05-16 14:34:55 +02001221 self.assert_equal(counter_value, expected_value,
1222 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001223
Ole Troan233e4682019-05-16 15:01:34 +02001224 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001225 counter_value = self.statistics[counter].sum()
Ole Troan233e4682019-05-16 15:01:34 +02001226 self.assert_equal(counter_value, expected_value,
1227 "error counter `%s'" % counter)
1228
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001229 @classmethod
1230 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001231
1232 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1233 # * by Guido, only the main thread can be interrupted.
1234 # */
1235 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1236 if timeout == 0:
1237 # yield quantum
1238 if hasattr(os, 'sched_yield'):
1239 os.sched_yield()
1240 else:
1241 time.sleep(0)
1242 return
1243
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001244 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001245 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001246 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001247 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001248 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001249 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001250 "slept for %es instead of ~%es!",
1251 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001252
1253 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001254 "Finished sleep (%s) - slept %es (wanted %es)",
1255 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001256
Benoît Ganne56eccdb2021-08-20 09:18:31 +02001257 def virtual_sleep(self, timeout, remark=None):
1258 self.logger.debug("Moving VPP time by %s (%s)", timeout, remark)
1259 self.vapi.cli("set clock adjust %s" % timeout)
1260
Benoît Ganne8c45e512021-02-19 16:39:13 +01001261 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001262 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001263 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001264 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001265
Klement Sekeraad3187f2022-02-18 10:34:35 +00001266 def snapshot_stats(self, stats_diff):
1267 """Return snapshot of interesting stats based on diff dictionary."""
1268 stats_snapshot = {}
1269 for sw_if_index in stats_diff:
1270 for counter in stats_diff[sw_if_index]:
1271 stats_snapshot[counter] = self.statistics[counter]
1272 self.logger.debug(f"Took statistics stats_snapshot: {stats_snapshot}")
1273 return stats_snapshot
1274
1275 def compare_stats_with_snapshot(self, stats_diff, stats_snapshot):
1276 """Assert appropriate difference between current stats and snapshot."""
1277 for sw_if_index in stats_diff:
1278 for cntr, diff in stats_diff[sw_if_index].items():
1279 if sw_if_index == "err":
1280 self.assert_equal(
1281 self.statistics[cntr].sum(),
1282 stats_snapshot[cntr].sum() + diff,
1283 f"'{cntr}' counter value (previous value: "
1284 f"{stats_snapshot[cntr].sum()}, "
1285 f"expected diff: {diff})")
1286 else:
1287 try:
1288 self.assert_equal(
1289 self.statistics[cntr][:, sw_if_index].sum(),
1290 stats_snapshot[cntr][:, sw_if_index].sum() + diff,
1291 f"'{cntr}' counter value (previous value: "
1292 f"{stats_snapshot[cntr][:, sw_if_index].sum()}, "
1293 f"expected diff: {diff})")
1294 except IndexError:
1295 # if diff is 0, then this most probably a case where
1296 # test declares multiple interfaces but traffic hasn't
1297 # passed through this one yet - which means the counter
1298 # value is 0 and can be ignored
1299 if 0 != diff:
1300 raise
1301
Klement Sekera7c3275e2021-12-07 09:49:53 +00001302 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None,
Klement Sekeraad3187f2022-02-18 10:34:35 +00001303 stats_diff=None, trace=True, msg=None):
1304 if stats_diff:
1305 stats_snapshot = self.snapshot_stats(stats_diff)
1306
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001307 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001308
1309 try:
1310 if not timeout:
1311 timeout = 1
1312 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +00001313 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001314 timeout = 0.1
1315 finally:
1316 if trace:
1317 if msg:
1318 self.logger.debug(f"send_and_assert_no_replies: {msg}")
1319 self.logger.debug(self.vapi.cli("show trace"))
1320
1321 if stats_diff:
1322 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -08001323
Benoît Ganne8c45e512021-02-19 16:39:13 +01001324 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
Klement Sekeraad3187f2022-02-18 10:34:35 +00001325 trace=True, msg=None, stats_diff=None):
1326 if stats_diff:
1327 stats_snapshot = self.snapshot_stats(stats_diff)
1328
Neale Rannsd7603d92019-03-28 08:56:10 +00001329 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +00001330 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001331 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001332 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +02001333 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001334 if msg:
1335 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +02001336 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +00001337
1338 if stats_diff:
1339 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1340
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001341 return rx
1342
Neale Ranns699bea22022-02-17 09:22:16 +00001343 def send_and_expect_load_balancing(self, input, pkts, outputs,
1344 worker=None, trace=True):
1345 self.pg_send(input, pkts, worker=worker, trace=trace)
1346 rxs = []
1347 for oo in outputs:
1348 rx = oo._get_capture(1)
1349 self.assertNotEqual(0, len(rx))
1350 rxs.append(rx)
1351 if trace:
1352 self.logger.debug(self.vapi.cli("show trace"))
1353 return rxs
1354
Neale Ranns5c6dd172022-02-17 09:08:47 +00001355 def send_and_expect_some(self, intf, pkts, output,
1356 worker=None,
1357 trace=True):
1358 self.pg_send(intf, pkts, worker=worker, trace=trace)
1359 rx = output._get_capture(1)
1360 if trace:
1361 self.logger.debug(self.vapi.cli("show trace"))
1362 self.assertTrue(len(rx) > 0)
1363 self.assertTrue(len(rx) < len(pkts))
1364 return rx
1365
Klement Sekeraad3187f2022-02-18 10:34:35 +00001366 def send_and_expect_only(self, intf, pkts, output, timeout=None,
1367 stats_diff=None):
1368 if stats_diff:
1369 stats_snapshot = self.snapshot_stats(stats_diff)
1370
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001371 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001372 rx = output.get_capture(len(pkts))
1373 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001374 if not timeout:
1375 timeout = 1
1376 for i in self.pg_interfaces:
1377 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +00001378 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001379 timeout = 0.1
1380
Klement Sekeraad3187f2022-02-18 10:34:35 +00001381 if stats_diff:
1382 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1383
Neale Ranns52fae862018-01-08 04:41:42 -08001384 return rx
1385
Damjan Marionf56b77a2016-10-03 19:44:57 +02001386
juraj.linkes184870a2018-07-16 14:22:01 +02001387def get_testcase_doc_name(test):
1388 return getdoc(test.__class__).splitlines()[0]
1389
1390
Ole Trøan5ba91592018-11-22 10:01:09 +00001391def get_test_description(descriptions, test):
1392 short_description = test.shortDescription()
1393 if descriptions and short_description:
1394 return short_description
1395 else:
1396 return str(test)
1397
1398
juraj.linkes40dd73b2018-09-21 13:55:16 +02001399class TestCaseInfo(object):
1400 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1401 self.logger = logger
1402 self.tempdir = tempdir
1403 self.vpp_pid = vpp_pid
1404 self.vpp_bin_path = vpp_bin_path
1405 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001406
1407
Damjan Marionf56b77a2016-10-03 19:44:57 +02001408class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001409 """
1410 @property result_string
1411 String variable to store the test case result string.
1412 @property errors
1413 List variable containing 2-tuples of TestCase instances and strings
1414 holding formatted tracebacks. Each tuple represents a test which
1415 raised an unexpected exception.
1416 @property failures
1417 List variable containing 2-tuples of TestCase instances and strings
1418 holding formatted tracebacks. Each tuple represents a test where
1419 a failure was explicitly signalled using the TestCase.assert*()
1420 methods.
1421 """
1422
juraj.linkes40dd73b2018-09-21 13:55:16 +02001423 failed_test_cases_info = set()
1424 core_crash_test_cases_info = set()
1425 current_test_case_info = None
1426
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001427 def __init__(self, stream=None, descriptions=None, verbosity=None,
1428 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001429 """
Klement Sekerada505f62017-01-04 12:58:53 +01001430 :param stream File descriptor to store where to report test results.
1431 Set to the standard error stream by default.
1432 :param descriptions Boolean variable to store information if to use
1433 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001434 :param verbosity Integer variable to store required verbosity level.
1435 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001436 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001437 self.stream = stream
1438 self.descriptions = descriptions
1439 self.verbosity = verbosity
1440 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001441 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001442 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001443
Damjan Marionf56b77a2016-10-03 19:44:57 +02001444 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001445 """
1446 Record a test succeeded result
1447
1448 :param test:
1449
1450 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001451 if self.current_test_case_info:
1452 self.current_test_case_info.logger.debug(
1453 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1454 test._testMethodName,
1455 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001456 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001457 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001458
juraj.linkescae64f82018-09-19 15:01:47 +02001459 self.send_result_through_pipe(test, PASS)
1460
Klement Sekeraf62ae122016-10-11 11:47:09 +02001461 def addSkip(self, test, reason):
1462 """
1463 Record a test skipped.
1464
1465 :param test:
1466 :param reason:
1467
1468 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001469 if self.current_test_case_info:
1470 self.current_test_case_info.logger.debug(
1471 "--- addSkip() %s.%s(%s) called, reason is %s" %
1472 (test.__class__.__name__, test._testMethodName,
1473 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001474 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001475 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001476
Klement Sekera558ceab2021-04-08 19:37:41 +02001477 if reason == "not enough cpus":
1478 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1479 else:
1480 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001481
juraj.linkes40dd73b2018-09-21 13:55:16 +02001482 def symlink_failed(self):
1483 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001484 try:
Klement Sekerab23ffd72021-05-31 16:08:53 +02001485 failed_dir = config.failed_dir
juraj.linkes40dd73b2018-09-21 13:55:16 +02001486 link_path = os.path.join(
1487 failed_dir,
1488 '%s-FAILED' %
1489 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001490
1491 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001492 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001493 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001494 "os.symlink(%s, %s)" %
1495 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001496 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001497 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001498 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001499 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001500 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001501
Klement Sekeraf413bef2017-08-15 07:09:02 +02001502 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001503 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001504
juraj.linkescae64f82018-09-19 15:01:47 +02001505 def send_result_through_pipe(self, test, result):
1506 if hasattr(self, 'test_framework_result_pipe'):
1507 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001508 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001509 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001510
juraj.linkes40dd73b2018-09-21 13:55:16 +02001511 def log_error(self, test, err, fn_name):
1512 if self.current_test_case_info:
1513 if isinstance(test, unittest.suite._ErrorHolder):
1514 test_name = test.description
1515 else:
1516 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1517 test._testMethodName,
1518 test._testMethodDoc)
1519 self.current_test_case_info.logger.debug(
1520 "--- %s() %s called, err is %s" %
1521 (fn_name, test_name, err))
1522 self.current_test_case_info.logger.debug(
1523 "formatted exception is:\n%s" %
1524 "".join(format_exception(*err)))
1525
1526 def add_error(self, test, err, unittest_fn, error_type):
1527 if error_type == FAIL:
1528 self.log_error(test, err, 'addFailure')
1529 error_type_str = colorize("FAIL", RED)
1530 elif error_type == ERROR:
1531 self.log_error(test, err, 'addError')
1532 error_type_str = colorize("ERROR", RED)
1533 else:
1534 raise Exception('Error type %s cannot be used to record an '
1535 'error or a failure' % error_type)
1536
1537 unittest_fn(self, test, err)
1538 if self.current_test_case_info:
1539 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1540 (error_type_str,
1541 self.current_test_case_info.tempdir)
1542 self.symlink_failed()
1543 self.failed_test_cases_info.add(self.current_test_case_info)
1544 if is_core_present(self.current_test_case_info.tempdir):
1545 if not self.current_test_case_info.core_crash_test:
1546 if isinstance(test, unittest.suite._ErrorHolder):
1547 test_name = str(test)
1548 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001549 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001550 get_testcase_doc_name(test), test.id())
1551 self.current_test_case_info.core_crash_test = test_name
1552 self.core_crash_test_cases_info.add(
1553 self.current_test_case_info)
1554 else:
1555 self.result_string = '%s [no temp dir]' % error_type_str
1556
1557 self.send_result_through_pipe(test, error_type)
1558
Damjan Marionf56b77a2016-10-03 19:44:57 +02001559 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001560 """
1561 Record a test failed result
1562
1563 :param test:
1564 :param err: error message
1565
1566 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001567 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001568
Damjan Marionf56b77a2016-10-03 19:44:57 +02001569 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001570 """
1571 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001572
Klement Sekeraf62ae122016-10-11 11:47:09 +02001573 :param test:
1574 :param err: error message
1575
1576 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001577 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001578
Damjan Marionf56b77a2016-10-03 19:44:57 +02001579 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001580 """
1581 Get test description
1582
1583 :param test:
1584 :returns: test description
1585
1586 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001587 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001588
Damjan Marionf56b77a2016-10-03 19:44:57 +02001589 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001590 """
1591 Start a test
1592
1593 :param test:
1594
1595 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001596
1597 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001598 if test.__class__ in self.printed:
1599 return
1600
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001601 test_doc = getdoc(test)
1602 if not test_doc:
1603 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001604
Klement Sekeraea6236b2021-03-25 14:03:44 +01001605 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001606 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001607 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001608 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001609
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001610 # This block may overwrite the colorized title above,
1611 # but we want this to stand out and be fixed
1612 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekera558ceab2021-04-08 19:37:41 +02001613 test_title = colorize(
1614 f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001615
Naveen Joy6eaeea92021-09-09 17:57:02 -07001616 if test.has_tag(TestCaseTag.FIXME_ASAN):
1617 test_title = colorize(
1618 f"FIXME with ASAN: {test_title}", RED)
1619 test.skip_fixme_asan()
1620
Klement Sekeraea6236b2021-03-25 14:03:44 +01001621 if hasattr(test, 'vpp_worker_count'):
1622 if test.vpp_worker_count == 0:
1623 test_title += " [main thread only]"
1624 elif test.vpp_worker_count == 1:
1625 test_title += " [1 worker thread]"
1626 else:
1627 test_title += f" [{test.vpp_worker_count} worker threads]"
1628
Klement Sekera558ceab2021-04-08 19:37:41 +02001629 if test.__class__.skipped_due_to_cpu_lack:
1630 test_title = colorize(
1631 f"{test_title} [skipped - not enough cpus, "
1632 f"required={test.__class__.get_cpus_required()}, "
1633 f"available={max_vpp_cpus}]", YELLOW)
1634
1635 print(double_line_delim)
1636 print(test_title)
1637 print(double_line_delim)
1638 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001639
1640 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001641 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001642 unittest.TestResult.startTest(self, test)
1643 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001644 self.stream.writeln(
1645 "Starting " + self.getDescription(test) + " ...")
1646 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001647
Damjan Marionf56b77a2016-10-03 19:44:57 +02001648 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001649 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001650 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001651
1652 :param test:
1653
1654 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001655 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001656
Damjan Marionf56b77a2016-10-03 19:44:57 +02001657 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001658 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001659 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001660 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001661 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001662 else:
Ole Troan0c629322019-11-28 14:48:44 +01001663 self.stream.writeln("%-68s %4.2f %s" %
1664 (self.getDescription(test),
1665 time.time() - self.start_test,
1666 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001667
1668 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001669
Damjan Marionf56b77a2016-10-03 19:44:57 +02001670 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001671 """
1672 Print errors from running the test case
1673 """
juraj.linkesabec0122018-11-16 17:28:56 +01001674 if len(self.errors) > 0 or len(self.failures) > 0:
1675 self.stream.writeln()
1676 self.printErrorList('ERROR', self.errors)
1677 self.printErrorList('FAIL', self.failures)
1678
1679 # ^^ that is the last output from unittest before summary
1680 if not self.runner.print_summary:
1681 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1682 self.stream = devnull
1683 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001684
Damjan Marionf56b77a2016-10-03 19:44:57 +02001685 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001686 """
1687 Print error list to the output stream together with error type
1688 and test case description.
1689
1690 :param flavour: error type
1691 :param errors: iterable errors
1692
1693 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001694 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001695 self.stream.writeln(double_line_delim)
1696 self.stream.writeln("%s: %s" %
1697 (flavour, self.getDescription(test)))
1698 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001699 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001700
1701
Damjan Marionf56b77a2016-10-03 19:44:57 +02001702class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001703 """
Klement Sekera104543f2017-02-03 07:29:43 +01001704 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001705 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001706
Klement Sekeraf62ae122016-10-11 11:47:09 +02001707 @property
1708 def resultclass(self):
1709 """Class maintaining the results of the tests"""
1710 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001711
juraj.linkes184870a2018-07-16 14:22:01 +02001712 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001713 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001714 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001715 # ignore stream setting here, use hard-coded stdout to be in sync
1716 # with prints from VppTestCase methods ...
1717 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1718 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001719 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001720 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001721
juraj.linkesabec0122018-11-16 17:28:56 +01001722 self.orig_stream = self.stream
1723 self.resultclass.test_framework_result_pipe = result_pipe
1724
1725 self.print_summary = print_summary
1726
1727 def _makeResult(self):
1728 return self.resultclass(self.stream,
1729 self.descriptions,
1730 self.verbosity,
1731 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001732
Damjan Marionf56b77a2016-10-03 19:44:57 +02001733 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001734 """
1735 Run the tests
1736
1737 :param test:
1738
1739 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001740 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001741
1742 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001743 if not self.print_summary:
1744 self.stream = self.orig_stream
1745 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001746 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001747
1748
1749class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001750 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1751 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001752 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001753 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001754 if hasattr(self, 'testcase') and self.testcase.debug_all:
1755 if self.testcase.debug_gdbserver:
1756 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1757 .format(port=self.testcase.gdbserver_port)] + args
1758 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1759 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001760 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001761 self.app_name = os.path.basename(self.app_bin)
1762 if hasattr(self, 'role'):
1763 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001764 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001765 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001766 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001767 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001768
Dave Wallace24564332019-10-21 02:53:14 +00001769 def wait_for_enter(self):
1770 if not hasattr(self, 'testcase'):
1771 return
1772 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1773 print()
1774 print(double_line_delim)
1775 print("Spawned GDB Server for '{app}' with PID: {pid}"
1776 .format(app=self.app_name, pid=self.process.pid))
1777 elif self.testcase.debug_all and self.testcase.debug_gdb:
1778 print()
1779 print(double_line_delim)
1780 print("Spawned '{app}' with PID: {pid}"
1781 .format(app=self.app_name, pid=self.process.pid))
1782 else:
1783 return
1784 print(single_line_delim)
1785 print("You can debug '{app}' using:".format(app=self.app_name))
1786 if self.testcase.debug_gdbserver:
1787 print("sudo gdb " + self.app_bin +
1788 " -ex 'target remote localhost:{port}'"
1789 .format(port=self.testcase.gdbserver_port))
1790 print("Now is the time to attach gdb by running the above "
1791 "command, set up breakpoints etc., then resume from "
1792 "within gdb by issuing the 'continue' command")
1793 self.testcase.gdbserver_port += 1
1794 elif self.testcase.debug_gdb:
1795 print("sudo gdb " + self.app_bin +
1796 " -ex 'attach {pid}'".format(pid=self.process.pid))
1797 print("Now is the time to attach gdb by running the above "
1798 "command and set up breakpoints etc., then resume from"
1799 " within gdb by issuing the 'continue' command")
1800 print(single_line_delim)
1801 input("Press ENTER to continue running the testcase...")
1802
Neale Ranns812ed392017-10-16 04:20:13 -07001803 def run(self):
1804 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001805 if not os.path.exists(executable) or not os.access(
1806 executable, os.F_OK | os.X_OK):
1807 # Exit code that means some system file did not exist,
1808 # could not be opened, or had some other kind of error.
1809 self.result = os.EX_OSFILE
1810 raise EnvironmentError(
1811 "executable '%s' is not found or executable." % executable)
Dave Wallace4ee17d82021-05-20 14:01:51 -04001812 self.logger.debug("Running executable '{app}': '{cmd}'"
1813 .format(app=self.app_name,
1814 cmd=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001815 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001816 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001817 env["CK_LOG_FILE_NAME"] = "-"
1818 self.process = subprocess.Popen(
Dave Wallaceae8d3322021-05-18 17:12:16 -04001819 ['stdbuf', '-o0', '-e0'] + self.args, shell=False, env=env,
1820 preexec_fn=os.setpgrp, stdout=subprocess.PIPE,
1821 stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001822 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001823 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001824 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001825 self.logger.info("Return code is `%s'" % self.process.returncode)
1826 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001827 self.logger.info("Executable `{app}' wrote to stdout:"
1828 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001829 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001830 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001831 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001832 self.logger.info("Executable `{app}' wrote to stderr:"
1833 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001834 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001835 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001836 self.logger.info(single_line_delim)
1837 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001838
Klement Sekera6aa58b72019-05-16 14:34:55 +02001839
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001840if __name__ == '__main__':
1841 pass