blob: 8f9a45b6c3a4ae03cc2c574abf116cd81a093633 [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
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020040from log import (
41 RED,
42 GREEN,
43 YELLOW,
44 double_line_delim,
45 single_line_delim,
46 get_logger,
47 colorize,
48)
Ole Trøan162989e2018-11-26 10:27:50 +000049from vpp_object import VppObjectRegistry
50from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020051from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
52from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
53from scapy.layers.inet6 import ICMPv6EchoReply
Naveen Joyc872cec2022-08-30 13:59:03 -070054from vpp_running import use_running
Klement Sekera47f35272023-03-29 16:04:58 +020055from test_result_code import TestResultCode
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080056
Klement Sekera558ceab2021-04-08 19:37:41 +020057
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050058logger = logging.getLogger(__name__)
59
60# Set up an empty logger for the testcase that can be overridden as necessary
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020061null_logger = logging.getLogger("VppTestCase")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050062null_logger.addHandler(logging.NullHandler())
63
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040064
Klement Sekerab23ffd72021-05-31 16:08:53 +020065if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010066 import debug_internal
67
Klement Sekeraf62ae122016-10-11 11:47:09 +020068"""
69 Test framework module.
70
71 The module provides a set of tools for constructing and running tests and
72 representing the results.
73"""
74
Klement Sekeraf62ae122016-10-11 11:47:09 +020075
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040076class VppDiedError(Exception):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020077 """exception for reporting that the subprocess has died."""
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040078
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020079 signals_by_value = {
80 v: k
81 for k, v in signal.__dict__.items()
82 if k.startswith("SIG") and not k.startswith("SIG_")
83 }
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040084
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040085 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040086 self.rv = rv
87 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040088 self.testcase = testcase
89 self.method_name = method_name
90
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040091 try:
92 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -040093 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040094 pass
95
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040096 if testcase is None and method_name is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020097 in_msg = ""
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040098 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020099 in_msg = " while running %s.%s" % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400100
Klement Sekera79a31db2021-03-12 18:16:10 +0100101 if self.rv:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200102 msg = "VPP subprocess died unexpectedly%s with return code: %d%s." % (
103 in_msg,
104 self.rv,
105 " [%s]" % (self.signal_name if self.signal_name is not None else ""),
106 )
Klement Sekera79a31db2021-03-12 18:16:10 +0100107 else:
108 msg = "VPP subprocess died unexpectedly%s." % in_msg
109
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400110 super(VppDiedError, self).__init__(msg)
111
112
Damjan Marionf56b77a2016-10-03 19:44:57 +0200113class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200114 """Private class to create packet info object.
115
116 Help process information about the next packet.
117 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200118 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200119
Matej Klotton86d87c42016-11-11 11:38:55 +0100120 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200121 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100122 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200123 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100124 #: Store the index of the destination packet generator interface
125 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200126 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100127 #: Store expected ip version
128 ip = -1
129 #: Store expected upper protocol
130 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100131 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200132 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200133
Matej Klotton16a14cd2016-12-07 15:09:13 +0100134 def __eq__(self, other):
135 index = self.index == other.index
136 src = self.src == other.src
137 dst = self.dst == other.dst
138 data = self.data == other.data
139 return index and src and dst and data
140
Klement Sekeraf62ae122016-10-11 11:47:09 +0200141
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100142def pump_output(testclass):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200143 """pump output from vpp stdout/stderr to proper queues"""
Dave Wallace670724c2022-09-20 21:52:18 -0400144 if not hasattr(testclass, "vpp"):
145 return
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100146 stdout_fragment = ""
147 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400148 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200149 readable = select.select(
150 [
151 testclass.vpp.stdout.fileno(),
152 testclass.vpp.stderr.fileno(),
153 testclass.pump_thread_wakeup_pipe[0],
154 ],
155 [],
156 [],
157 )[0]
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100158 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100159 read = os.read(testclass.vpp.stdout.fileno(), 102400)
160 if len(read) > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200161 split = read.decode("ascii", errors="backslashreplace").splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100162 if len(stdout_fragment) > 0:
163 split[0] = "%s%s" % (stdout_fragment, split[0])
164 if len(split) > 0 and split[-1].endswith("\n"):
165 limit = None
166 else:
167 limit = -1
168 stdout_fragment = split[-1]
169 testclass.vpp_stdout_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200170 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100171 for line in split[:limit]:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200172 testclass.logger.info("VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100173 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100174 read = os.read(testclass.vpp.stderr.fileno(), 102400)
175 if len(read) > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200176 split = read.decode("ascii", errors="backslashreplace").splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100177 if len(stderr_fragment) > 0:
178 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200179 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100180 limit = None
181 else:
182 limit = -1
183 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200184
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100185 testclass.vpp_stderr_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200186 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100187 for line in split[:limit]:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200188 testclass.logger.error("VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800189 # ignoring the dummy pipe here intentionally - the
190 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200191
192
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800193def _is_platform_aarch64():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200194 return platform.machine() == "aarch64"
juraj.linkes68ebc832018-11-29 09:37:08 +0100195
Klement Sekera6aa58b72019-05-16 14:34:55 +0200196
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800197is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100198
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800199
Dave Wallacee95b2462022-09-18 22:28:44 -0400200def _is_distro_ubuntu2204():
201 with open("/etc/os-release") as f:
202 for line in f.readlines():
203 if "jammy" in line:
204 return True
205 return False
206
207
208is_distro_ubuntu2204 = _is_distro_ubuntu2204()
209
210
Dave Wallace670724c2022-09-20 21:52:18 -0400211def _is_distro_debian11():
212 with open("/etc/os-release") as f:
213 for line in f.readlines():
214 if "bullseye" in line:
215 return True
216 return False
217
218
219is_distro_debian11 = _is_distro_debian11()
220
221
Klement Sekera909a6a12017-08-08 04:33:53 +0200222class KeepAliveReporter(object):
223 """
224 Singleton object which reports test start to parent process
225 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200226
Klement Sekera909a6a12017-08-08 04:33:53 +0200227 _shared_state = {}
228
229 def __init__(self):
230 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800231 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200232
233 @property
234 def pipe(self):
235 return self._pipe
236
237 @pipe.setter
238 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800239 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200240 raise Exception("Internal error - pipe should only be set once.")
241 self._pipe = pipe
242
juraj.linkes40dd73b2018-09-21 13:55:16 +0200243 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200244 """
245 Write current test tmpdir & desc to keep-alive pipe to signal liveness
246 """
Dave Wallace670724c2022-09-20 21:52:18 -0400247 if not hasattr(test, "vpp") or self.pipe is None:
Klement Sekera3f6ff192017-08-11 06:56:05 +0200248 # if not running forked..
249 return
250
Klement Sekera909a6a12017-08-08 04:33:53 +0200251 if isclass(test):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200252 desc = "%s (%s)" % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200253 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200254 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200255
Klement Sekerab23ffd72021-05-31 16:08:53 +0200256 self.pipe.send((desc, config.vpp, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200257
258
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000259class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000260 # marks the suites that must run at the end
261 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000262 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000263 # marks the suites broken on VPP multi-worker
264 FIXME_VPP_WORKERS = 2
Naveen Joy6eaeea92021-09-09 17:57:02 -0700265 # marks the suites broken when ASan is enabled
266 FIXME_ASAN = 3
Dave Wallacee95b2462022-09-18 22:28:44 -0400267 # marks suites broken on Ubuntu-22.04
268 FIXME_UBUNTU2204 = 4
Dave Wallace670724c2022-09-20 21:52:18 -0400269 # marks suites broken on Debian-11
270 FIXME_DEBIAN11 = 5
Dave Wallace8a0a9d22022-10-04 22:02:49 -0400271 # marks suites broken on debug vpp image
272 FIXME_VPP_DEBUG = 6
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000273
274
275def create_tag_decorator(e):
276 def decorator(cls):
277 try:
278 cls.test_tags.append(e)
279 except AttributeError:
280 cls.test_tags = [e]
281 return cls
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200282
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000283 return decorator
284
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000285
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000286tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000287tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Naveen Joy6eaeea92021-09-09 17:57:02 -0700288tag_fixme_asan = create_tag_decorator(TestCaseTag.FIXME_ASAN)
Dave Wallacee95b2462022-09-18 22:28:44 -0400289tag_fixme_ubuntu2204 = create_tag_decorator(TestCaseTag.FIXME_UBUNTU2204)
Dave Wallace670724c2022-09-20 21:52:18 -0400290tag_fixme_debian11 = create_tag_decorator(TestCaseTag.FIXME_DEBIAN11)
Dave Wallace8a0a9d22022-10-04 22:02:49 -0400291tag_fixme_vpp_debug = create_tag_decorator(TestCaseTag.FIXME_VPP_DEBUG)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000292
293
Klement Sekerae2636852021-03-16 12:52:12 +0100294class DummyVpp:
295 returncode = None
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200296 pid = 0xCAFEBAFE
Klement Sekerae2636852021-03-16 12:52:12 +0100297
298 def poll(self):
299 pass
300
301 def terminate(self):
302 pass
303
304
Klement Sekera558ceab2021-04-08 19:37:41 +0200305class CPUInterface(ABC):
306 cpus = []
307 skipped_due_to_cpu_lack = False
308
309 @classmethod
310 @abstractmethod
311 def get_cpus_required(cls):
312 pass
313
314 @classmethod
315 def assign_cpus(cls, cpus):
316 cls.cpus = cpus
317
318
Naveen Joyc872cec2022-08-30 13:59:03 -0700319@use_running
Klement Sekera558ceab2021-04-08 19:37:41 +0200320class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100321 """This subclass is a base class for VPP test cases that are implemented as
322 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200323 """
324
Arthur de Kerhordb023802021-03-11 10:26:54 -0800325 extra_vpp_statseg_config = ""
Klement Sekerad3e0d102023-01-26 12:35:35 +0100326 extra_vpp_config = []
Ole Troana45dc072018-12-21 16:04:22 +0100327 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500328 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400329 vapi_response_timeout = 5
Klement Sekera140af152022-02-18 10:30:51 +0000330 remove_configured_vpp_objects_on_tear_down = True
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100331
Klement Sekeraf62ae122016-10-11 11:47:09 +0200332 @property
333 def packet_infos(self):
334 """List of packet infos"""
335 return self._packet_infos
336
Klement Sekeradab231a2016-12-21 08:50:14 +0100337 @classmethod
338 def get_packet_count_for_if_idx(cls, dst_if_index):
339 """Get the number of packet info for specified destination if index"""
340 if dst_if_index in cls._packet_count_for_dst_if_idx:
341 return cls._packet_count_for_dst_if_idx[dst_if_index]
342 else:
343 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200344
345 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000346 def has_tag(cls, tag):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200347 """if the test case has a given tag - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000348 try:
349 return tag in cls.test_tags
350 except AttributeError:
351 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000352 return False
353
354 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000355 def is_tagged_run_solo(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200356 """if the test case class is timing-sensitive - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000357 return cls.has_tag(TestCaseTag.RUN_SOLO)
358
359 @classmethod
Naveen Joy6eaeea92021-09-09 17:57:02 -0700360 def skip_fixme_asan(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200361 """if @tag_fixme_asan & ASan is enabled - mark for skip"""
Naveen Joy6eaeea92021-09-09 17:57:02 -0700362 if cls.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200363 vpp_extra_cmake_args = os.environ.get("VPP_EXTRA_CMAKE_ARGS", "")
364 if "DVPP_ENABLE_SANITIZE_ADDR=ON" in vpp_extra_cmake_args:
Naveen Joy6eaeea92021-09-09 17:57:02 -0700365 cls = unittest.skip("Skipping @tag_fixme_asan tests")(cls)
366
367 @classmethod
Dave Wallacee95b2462022-09-18 22:28:44 -0400368 def skip_fixme_ubuntu2204(cls):
369 """if distro is ubuntu 22.04 and @tag_fixme_ubuntu2204 mark for skip"""
370 if cls.has_tag(TestCaseTag.FIXME_UBUNTU2204):
371 cls = unittest.skip("Skipping @tag_fixme_ubuntu2204 tests")(cls)
372
373 @classmethod
Dave Wallace670724c2022-09-20 21:52:18 -0400374 def skip_fixme_debian11(cls):
375 """if distro is Debian-11 and @tag_fixme_debian11 mark for skip"""
376 if cls.has_tag(TestCaseTag.FIXME_DEBIAN11):
377 cls = unittest.skip("Skipping @tag_fixme_debian11 tests")(cls)
378
379 @classmethod
Dave Wallace8a0a9d22022-10-04 22:02:49 -0400380 def skip_fixme_vpp_debug(cls):
381 cls = unittest.skip("Skipping @tag_fixme_vpp_debug tests")(cls)
382
383 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200384 def instance(cls):
385 """Return the instance of this testcase"""
386 return cls.test_instance
387
Damjan Marionf56b77a2016-10-03 19:44:57 +0200388 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200389 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000390 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200391 cls.debug_core = False
392 cls.debug_gdb = False
393 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000394 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100395 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200396 if d is None:
397 return
398 dl = d.lower()
399 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200400 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000401 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200402 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000403 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200404 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100405 elif dl == "attach":
406 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200407 else:
408 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000409 if dl == "gdb-all" or dl == "gdbserver-all":
410 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200411
Klement Sekera558ceab2021-04-08 19:37:41 +0200412 @classmethod
413 def get_vpp_worker_count(cls):
414 if not hasattr(cls, "vpp_worker_count"):
415 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
416 cls.vpp_worker_count = 0
417 else:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200418 cls.vpp_worker_count = config.vpp_worker_count
Klement Sekera558ceab2021-04-08 19:37:41 +0200419 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200420
Klement Sekera558ceab2021-04-08 19:37:41 +0200421 @classmethod
422 def get_cpus_required(cls):
423 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200424
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800425 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200426 def setUpConstants(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200427 """Set-up the test case class based on environment variables"""
Klement Sekerab23ffd72021-05-31 16:08:53 +0200428 cls.step = config.step
429 cls.plugin_path = ":".join(config.vpp_plugin_dir)
430 cls.test_plugin_path = ":".join(config.vpp_test_plugin_dir)
431 cls.extern_plugin_path = ":".join(config.extern_plugin_dir)
Ole Troana45dc072018-12-21 16:04:22 +0100432 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100433 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100434 debug_cli = "cli-listen localhost:5002"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200435 size = re.search(r"\d+[gG]", config.coredump_size)
436 if size:
437 coredump_size = f"coredump-size {config.coredump_size}".lower()
438 else:
Ole Troana45dc072018-12-21 16:04:22 +0100439 coredump_size = "coredump-size unlimited"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200440 default_variant = config.variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000441 if default_variant is not None:
Benoît Ganne09ef5922022-07-29 10:52:34 +0200442 default_variant = "default { variant %s 100 }" % default_variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000443 else:
444 default_variant = ""
445
Klement Sekerab23ffd72021-05-31 16:08:53 +0200446 api_fuzzing = config.api_fuzz
Dave Barach77841402020-04-29 17:04:10 -0400447 if api_fuzzing is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200448 api_fuzzing = "off"
Dave Barach77841402020-04-29 17:04:10 -0400449
Klement Sekera8d815022021-03-15 16:58:10 +0100450 cls.vpp_cmdline = [
Klement Sekerab23ffd72021-05-31 16:08:53 +0200451 config.vpp,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200452 "unix",
453 "{",
454 "nodaemon",
455 debug_cli,
456 "full-coredump",
457 coredump_size,
458 "runtime-dir",
459 cls.tempdir,
Klement Sekera8d815022021-03-15 16:58:10 +0100460 "}",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200461 "api-trace",
462 "{",
463 "on",
464 "}",
465 "api-segment",
466 "{",
467 "prefix",
468 cls.get_api_segment_prefix(),
469 "}",
470 "cpu",
471 "{",
472 "main-core",
473 str(cls.cpus[0]),
474 ]
475 if cls.extern_plugin_path not in (None, ""):
476 cls.extra_vpp_plugin_config.append("add-path %s" % cls.extern_plugin_path)
477 if cls.get_vpp_worker_count():
478 cls.vpp_cmdline.extend(
479 ["corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])]
480 )
481 cls.vpp_cmdline.extend(
482 [
483 "}",
484 "physmem",
485 "{",
486 "max-size",
487 "32m",
488 "}",
489 "statseg",
490 "{",
491 "socket-name",
492 cls.get_stats_sock_path(),
493 cls.extra_vpp_statseg_config,
494 "}",
495 "socksvr",
496 "{",
497 "socket-name",
498 cls.get_api_sock_path(),
499 "}",
500 "node { ",
501 default_variant,
502 "}",
503 "api-fuzz {",
504 api_fuzzing,
505 "}",
506 "plugins",
507 "{",
508 "plugin",
509 "dpdk_plugin.so",
510 "{",
511 "disable",
512 "}",
513 "plugin",
514 "rdma_plugin.so",
515 "{",
516 "disable",
517 "}",
518 "plugin",
519 "lisp_unittest_plugin.so",
520 "{",
521 "enable",
522 "}",
523 "plugin",
524 "unittest_plugin.so",
525 "{",
526 "enable",
527 "}",
528 ]
529 + cls.extra_vpp_plugin_config
530 + [
531 "}",
532 ]
533 )
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000534
Klement Sekerad3e0d102023-01-26 12:35:35 +0100535 if cls.extra_vpp_config is not None:
536 cls.vpp_cmdline.extend(cls.extra_vpp_config)
Dave Barach7d31ab22019-05-08 19:18:18 -0400537
Klement Sekerae2636852021-03-16 12:52:12 +0100538 if not cls.debug_attach:
539 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
540 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200541
542 @classmethod
543 def wait_for_enter(cls):
544 if cls.debug_gdbserver:
545 print(double_line_delim)
546 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
547 elif cls.debug_gdb:
548 print(double_line_delim)
549 print("Spawned VPP with PID: %d" % cls.vpp.pid)
550 else:
551 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
552 return
553 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000554 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200555 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200556 print(
557 f"sudo gdb {config.vpp} "
558 f"-ex 'target remote localhost:{cls.gdbserver_port}'"
559 )
560 print(
561 "Now is the time to attach gdb by running the above "
562 "command, set up breakpoints etc., then resume VPP from "
563 "within gdb by issuing the 'continue' command"
564 )
Dave Wallace24564332019-10-21 02:53:14 +0000565 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200566 elif cls.debug_gdb:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200567 print(f"sudo gdb {config.vpp} -ex 'attach {cls.vpp.pid}'")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200568 print(
569 "Now is the time to attach gdb by running the above "
570 "command and set up breakpoints etc., then resume VPP from"
571 " within gdb by issuing the 'continue' command"
572 )
Klement Sekera277b89c2016-10-28 13:20:27 +0200573 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800574 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200575
576 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100577 def attach_vpp(cls):
578 cls.vpp = DummyVpp()
579
580 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200581 def run_vpp(cls):
Dave Wallace670724c2022-09-20 21:52:18 -0400582 if (
583 is_distro_ubuntu2204 == True and cls.has_tag(TestCaseTag.FIXME_UBUNTU2204)
584 ) or (is_distro_debian11 == True and cls.has_tag(TestCaseTag.FIXME_DEBIAN11)):
585 return
Klement Sekera558ceab2021-04-08 19:37:41 +0200586 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200587 cmdline = cls.vpp_cmdline
588
589 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200590 gdbserver = "/usr/bin/gdbserver"
591 if not os.path.isfile(gdbserver) or not os.access(gdbserver, os.X_OK):
592 raise Exception(
593 "gdbserver binary '%s' does not exist or is "
594 "not executable" % gdbserver
595 )
Klement Sekera931be3a2016-11-03 05:36:01 +0100596
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200597 cmdline = [
598 gdbserver,
599 "localhost:{port}".format(port=cls.gdbserver_port),
600 ] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200601 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
602
Klement Sekera931be3a2016-11-03 05:36:01 +0100603 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200604 cls.vpp = subprocess.Popen(
605 cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE
606 )
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800607 except subprocess.CalledProcessError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200608 cls.logger.critical(
609 "Subprocess returned with non-0 return code: (%s)", e.returncode
610 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800611 raise
612 except OSError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200613 cls.logger.critical(
614 "Subprocess returned with OS error: (%s) %s", e.errno, e.strerror
615 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800616 raise
617 except Exception as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200618 cls.logger.exception("Subprocess returned unexpected from %s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100619 raise
620
Klement Sekera277b89c2016-10-28 13:20:27 +0200621 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100622
Damjan Marionf56b77a2016-10-03 19:44:57 +0200623 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000624 def wait_for_coredump(cls):
625 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400626 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000627 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400628 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000629 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400630 ok = False
631 while time.time() < deadline:
632 cls.sleep(1)
633 size = curr_size
634 curr_size = os.path.getsize(corefile)
635 if size == curr_size:
636 ok = True
637 break
638 if not ok:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200639 cls.logger.error(
640 "Timed out waiting for coredump to complete: %s", corefile
641 )
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400642 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200643 cls.logger.error("Coredump complete: %s, size %d", corefile, curr_size)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400644
645 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100646 def get_stats_sock_path(cls):
647 return "%s/stats.sock" % cls.tempdir
648
649 @classmethod
650 def get_api_sock_path(cls):
651 return "%s/api.sock" % cls.tempdir
652
653 @classmethod
654 def get_api_segment_prefix(cls):
655 return os.path.basename(cls.tempdir) # Only used for VAPI
656
657 @classmethod
658 def get_tempdir(cls):
Klement Sekerab3fc6582022-03-10 11:47:45 +0100659 if cls.debug_attach:
660 tmpdir = f"{config.tmp_dir}/unittest-attach-gdb"
661 else:
662 tmpdir = f"{config.tmp_dir}/vpp-unittest-{cls.__name__}"
663 if config.wipe_tmp_dir:
664 shutil.rmtree(tmpdir, ignore_errors=True)
665 os.mkdir(tmpdir)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200666 return tmpdir
667
668 @classmethod
669 def create_file_handler(cls):
670 if config.log_dir is None:
671 cls.file_handler = FileHandler(f"{cls.tempdir}/log.txt")
672 return
673
674 logdir = f"{config.log_dir}/vpp-unittest-{cls.__name__}"
675 if config.wipe_tmp_dir:
676 shutil.rmtree(logdir, ignore_errors=True)
677 os.mkdir(logdir)
678 cls.file_handler = FileHandler(f"{logdir}/log.txt")
Klement Sekerae2636852021-03-16 12:52:12 +0100679
680 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200681 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200682 """
683 Perform class setup before running the testcase
684 Remove shared memory files, start vpp and connect the vpp-api
685 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800686 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100687 cls.logger = get_logger(cls.__name__)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200688 random.seed(config.rnd_seed)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200689 if hasattr(cls, "parallel_handler"):
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100690 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100691 cls.logger.propagate = False
Klement Sekerab23ffd72021-05-31 16:08:53 +0200692 cls.set_debug_flags(config.debug)
Klement Sekerae2636852021-03-16 12:52:12 +0100693 cls.tempdir = cls.get_tempdir()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200694 cls.create_file_handler()
Klement Sekera027dbd52017-04-11 06:01:53 +0200695 cls.file_handler.setFormatter(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200696 Formatter(fmt="%(asctime)s,%(msecs)03d %(message)s", datefmt="%H:%M:%S")
697 )
Klement Sekera027dbd52017-04-11 06:01:53 +0200698 cls.file_handler.setLevel(DEBUG)
699 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100700 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200701 os.chdir(cls.tempdir)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200702 cls.logger.info(
703 "Temporary dir is %s, api socket is %s",
704 cls.tempdir,
705 cls.get_api_sock_path(),
706 )
Klement Sekerab23ffd72021-05-31 16:08:53 +0200707 cls.logger.debug("Random seed is %s", config.rnd_seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200708 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100709 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200710 cls._pcaps = []
711 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200712 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100713 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100714 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200715 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200716 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200717 # need to catch exceptions here because if we raise, then the cleanup
718 # doesn't get called and we might end with a zombie vpp
719 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100720 if cls.debug_attach:
721 cls.attach_vpp()
722 else:
723 cls.run_vpp()
Dave Wallace670724c2022-09-20 21:52:18 -0400724 if not hasattr(cls, "vpp"):
725 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200726 cls.reporter.send_keep_alive(cls, "setUpClass")
juraj.linkes40dd73b2018-09-21 13:55:16 +0200727 VppTestResult.current_test_case_info = TestCaseInfo(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200728 cls.logger, cls.tempdir, cls.vpp.pid, config.vpp
729 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100730 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100731 cls.vpp_stderr_deque = deque()
Naveen Joyc872cec2022-08-30 13:59:03 -0700732 # Pump thread in a non-debug-attached & not running-vpp
733 if not cls.debug_attach and not hasattr(cls, "running_vpp"):
Klement Sekerae2636852021-03-16 12:52:12 +0100734 cls.pump_thread_stop_flag = Event()
735 cls.pump_thread_wakeup_pipe = os.pipe()
736 cls.pump_thread = Thread(target=pump_output, args=(cls,))
737 cls.pump_thread.daemon = True
738 cls.pump_thread.start()
739 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400740 cls.vapi_response_timeout = 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200741 cls.vapi = VppPapiProvider(cls.__name__, cls, cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100742 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400743 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100744 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400745 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100746 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100747 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200748 try:
749 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100750 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200751 cls.vpp_startup_failed = True
752 cls.logger.critical(
753 "VPP died shortly after startup, check the"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200754 " output to standard error for possible cause"
755 )
Klement Sekera3747c752017-04-10 06:30:17 +0200756 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100757 try:
758 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100759 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500760 cls.logger.debug("Exception connecting to vapi: %s" % e)
761 cls.vapi.disconnect()
762
Klement Sekera085f5c02016-11-24 01:59:16 +0100763 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200764 print(
765 colorize(
766 "You're running VPP inside gdbserver but "
767 "VPP-API connection failed, did you forget "
768 "to 'continue' VPP from within gdb?",
769 RED,
770 )
771 )
Ole Troan4376ab22021-03-03 10:40:05 +0100772 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100773 if cls.debug_attach:
774 last_line = cls.vapi.cli("show thread").split("\n")[-2]
775 cls.vpp_worker_count = int(last_line.split(" ")[0])
776 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400777 except vpp_papi.VPPRuntimeError as e:
778 cls.logger.debug("%s" % e)
779 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100780 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000781 except Exception as e:
782 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400783 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100784 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200785
Damjan Marionf56b77a2016-10-03 19:44:57 +0200786 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500787 def _debug_quit(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200788 if cls.debug_gdbserver or cls.debug_gdb:
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500789 try:
790 cls.vpp.poll()
791
792 if cls.vpp.returncode is None:
793 print()
794 print(double_line_delim)
795 print("VPP or GDB server is still running")
796 print(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200797 input(
798 "When done debugging, press ENTER to kill the "
799 "process and finish running the testcase..."
800 )
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500801 except AttributeError:
802 pass
803
804 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200805 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200806 """
807 Disconnect vpp-api, kill vpp and cleanup shared memory files
808 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500809 cls._debug_quit()
Naveen Joyc872cec2022-08-30 13:59:03 -0700810 if hasattr(cls, "running_vpp"):
811 cls.vpp.quit_vpp()
Klement Sekera277b89c2016-10-28 13:20:27 +0200812
juraj.linkes184870a2018-07-16 14:22:01 +0200813 # first signal that we want to stop the pump thread, then wake it up
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200814 if hasattr(cls, "pump_thread_stop_flag"):
juraj.linkes184870a2018-07-16 14:22:01 +0200815 cls.pump_thread_stop_flag.set()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200816 if hasattr(cls, "pump_thread_wakeup_pipe"):
817 os.write(cls.pump_thread_wakeup_pipe[1], b"ding dong wake up")
818 if hasattr(cls, "pump_thread"):
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100819 cls.logger.debug("Waiting for pump thread to stop")
820 cls.pump_thread.join()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200821 if hasattr(cls, "vpp_stderr_reader_thread"):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500822 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100823 cls.vpp_stderr_reader_thread.join()
824
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200825 if hasattr(cls, "vpp"):
826 if hasattr(cls, "vapi"):
Ole Troanfd574082019-11-27 23:12:48 +0100827 cls.logger.debug(cls.vapi.vpp.get_stats())
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200828 cls.logger.debug("Disconnecting class vapi client on %s", cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100829 cls.vapi.disconnect()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200830 cls.logger.debug("Deleting class vapi attribute on %s", cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100831 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200832 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100833 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000834 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100835 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400836 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100837 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100838 try:
839 outs, errs = cls.vpp.communicate(timeout=5)
840 except subprocess.TimeoutExpired:
841 cls.vpp.kill()
842 outs, errs = cls.vpp.communicate()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200843 cls.logger.debug("Deleting class vpp attribute on %s", cls.__name__)
Naveen Joyc872cec2022-08-30 13:59:03 -0700844 if not cls.debug_attach and not hasattr(cls, "running_vpp"):
Klement Sekerae2636852021-03-16 12:52:12 +0100845 cls.vpp.stdout.close()
846 cls.vpp.stderr.close()
Naveen Joyc872cec2022-08-30 13:59:03 -0700847 # If vpp is a dynamic attribute set by the func use_running,
848 # deletion will result in an AttributeError that we can
849 # safetly pass.
850 try:
851 del cls.vpp
852 except AttributeError:
853 pass
Damjan Marionf56b77a2016-10-03 19:44:57 +0200854
Klement Sekera3747c752017-04-10 06:30:17 +0200855 if cls.vpp_startup_failed:
856 stdout_log = cls.logger.info
857 stderr_log = cls.logger.critical
858 else:
859 stdout_log = cls.logger.info
860 stderr_log = cls.logger.info
861
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200862 if hasattr(cls, "vpp_stdout_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200863 stdout_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200864 stdout_log("VPP output to stdout while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200865 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100866 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200867 with open(cls.tempdir + "/vpp_stdout.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200868 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200869 stdout_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200870 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200871
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200872 if hasattr(cls, "vpp_stderr_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200873 stderr_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200874 stderr_log("VPP output to stderr while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200875 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100876 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200877 with open(cls.tempdir + "/vpp_stderr.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200878 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200879 stderr_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200880 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200881
Damjan Marionf56b77a2016-10-03 19:44:57 +0200882 @classmethod
883 def tearDownClass(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200884 """Perform final cleanup after running all tests in this test-case"""
885 cls.logger.debug("--- tearDownClass() for %s called ---" % cls.__name__)
Dave Wallace670724c2022-09-20 21:52:18 -0400886 if not hasattr(cls, "vpp"):
887 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200888 cls.reporter.send_keep_alive(cls, "tearDownClass")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200889 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200890 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100891 cls.reset_packet_infos()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200892 if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +0100893 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200894
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700895 def show_commands_at_teardown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200896 """Allow subclass specific teardown logging additions."""
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700897 self.logger.info("--- No test specific show commands provided. ---")
898
Damjan Marionf56b77a2016-10-03 19:44:57 +0200899 def tearDown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200900 """Show various debug prints after each test"""
901 self.logger.debug(
902 "--- tearDown() for %s.%s(%s) called ---"
903 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
904 )
Dave Wallace670724c2022-09-20 21:52:18 -0400905 if not hasattr(self, "vpp"):
906 return
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700907
908 try:
909 if not self.vpp_dead:
910 self.logger.debug(self.vapi.cli("show trace max 1000"))
911 self.logger.info(self.vapi.ppcli("show interface"))
912 self.logger.info(self.vapi.ppcli("show hardware"))
913 self.logger.info(self.statistics.set_errors_str())
914 self.logger.info(self.vapi.ppcli("show run"))
915 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400916 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700917 self.logger.info("Logging testcase specific show commands.")
918 self.show_commands_at_teardown()
Klement Sekera140af152022-02-18 10:30:51 +0000919 if self.remove_configured_vpp_objects_on_tear_down:
920 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500921 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000922 m = self._testMethodName
923 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500924 tmp_api_trace = "/tmp/%s" % api_trace
925 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
926 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200927 self.logger.info("Moving %s to %s\n" % (tmp_api_trace, vpp_api_trace_log))
Dmitry Valter71d02aa2023-01-27 12:49:55 +0000928 shutil.move(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100929 except VppTransportSocketIOError:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200930 self.logger.debug(
931 "VppTransportSocketIOError: Vpp dead. Cannot log show commands."
932 )
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700933 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100934 else:
935 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200936
Damjan Marionf56b77a2016-10-03 19:44:57 +0200937 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200938 """Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800939 super(VppTestCase, self).setUp()
Dave Wallace670724c2022-09-20 21:52:18 -0400940 if not hasattr(self, "vpp"):
941 return
Klement Sekera909a6a12017-08-08 04:33:53 +0200942 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100943 if self.vpp_dead:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200944 raise VppDiedError(
945 rv=None,
946 testcase=self.__class__.__name__,
947 method_name=self._testMethodName,
948 )
949 self.sleep(0.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100950 self.vpp_stdout_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200951 "--- test setUp() for %s.%s(%s) starts here ---\n"
952 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
953 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100954 self.vpp_stderr_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200955 "--- test setUp() for %s.%s(%s) starts here ---\n"
956 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
957 )
Klement Sekeraf62ae122016-10-11 11:47:09 +0200958 self.vapi.cli("clear trace")
959 # store the test instance inside the test class - so that objects
960 # holding the class can access instance methods (like assertEqual)
961 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200962
Damjan Marionf56b77a2016-10-03 19:44:57 +0200963 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200964 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200965 """
966 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200967
Klement Sekera75e7d132017-09-20 08:26:30 +0200968 :param interfaces: iterable interface indexes (if None,
969 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200970
Klement Sekeraf62ae122016-10-11 11:47:09 +0200971 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200972 if interfaces is None:
973 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200974 for i in interfaces:
975 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200976
Damjan Marionf56b77a2016-10-03 19:44:57 +0200977 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200978 def register_pcap(cls, intf, worker):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200979 """Register a pcap in the testclass"""
Klement Sekera9225dee2016-12-12 08:36:58 +0100980 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200981 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100982
983 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000984 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400985 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
986 # returns float("2.190522")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200987 timestr = cls.vapi.cli("show clock")
988 head, sep, tail = timestr.partition(",")
989 head, sep, tail = head.partition("Time now")
Dave Barach19718002020-03-11 10:31:36 -0400990 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000991
992 @classmethod
993 def sleep_on_vpp_time(cls, sec):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200994 """Sleep according to time in VPP world"""
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000995 # On a busy system with many processes
996 # we might end up with VPP time being slower than real world
997 # So take that into account when waiting for VPP to do something
998 start_time = cls.get_vpp_time()
999 while cls.get_vpp_time() - start_time < sec:
1000 cls.sleep(0.1)
1001
1002 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +01001003 def pg_start(cls, trace=True):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001004 """Enable the PG, wait till it is done, then clean up"""
Dave Wallace7b8b4652023-08-15 19:05:26 -04001005 for intf, worker in cls._old_pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001006 intf.handle_old_pcap_file(intf.get_in_path(worker), intf.in_history_counter)
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001007 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +01001008 if trace:
1009 cls.vapi.cli("clear trace")
1010 cls.vapi.cli("trace add pg-input 1000")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001011 cls.vapi.cli("packet-generator enable")
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +00001012 # PG, when starts, runs to completion -
1013 # so let's avoid a race condition,
1014 # and wait a little till it's done.
1015 # Then clean it up - and then be gone.
1016 deadline = time.time() + 300
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001017 while cls.vapi.cli("show packet-generator").find("Yes") != -1:
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +00001018 cls.sleep(0.01) # yield
1019 if time.time() > deadline:
1020 cls.logger.error("Timeout waiting for pg to stop")
1021 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001022 for intf, worker in cls._pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001023 cls.vapi.cli("packet-generator delete %s" % intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001024 cls._old_pcaps = cls._pcaps
1025 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001026
Damjan Marionf56b77a2016-10-03 19:44:57 +02001027 @classmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001028 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0, mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001029 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001030 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +02001031
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001032 :param interfaces: iterable indexes of the interfaces.
1033 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +02001034
Klement Sekeraf62ae122016-10-11 11:47:09 +02001035 """
1036 result = []
1037 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +00001038 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001039 setattr(cls, intf.name, intf)
1040 result.append(intf)
1041 cls.pg_interfaces = result
1042 return result
1043
Matej Klotton0178d522016-11-04 11:11:44 +01001044 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +00001045 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001046 if not hasattr(cls, "vpp"):
1047 cls.pg_interfaces = []
1048 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001049 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001050 return cls.create_pg_interfaces_internal(
1051 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP4
1052 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001053
1054 @classmethod
1055 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001056 if not hasattr(cls, "vpp"):
1057 cls.pg_interfaces = []
1058 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001059 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001060 return cls.create_pg_interfaces_internal(
1061 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP6
1062 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001063
1064 @classmethod
1065 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001066 if not hasattr(cls, "vpp"):
1067 cls.pg_interfaces = []
1068 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001069 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001070 return cls.create_pg_interfaces_internal(
1071 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1072 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001073
1074 @classmethod
1075 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001076 if not hasattr(cls, "vpp"):
1077 cls.pg_interfaces = []
1078 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001079 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001080 return cls.create_pg_interfaces_internal(
1081 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1082 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001083
1084 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +02001085 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +01001086 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001087 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001088
Klement Sekerab9ef2732018-06-24 22:49:33 +02001089 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001090 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001091 """
Dave Wallace670724c2022-09-20 21:52:18 -04001092 if not hasattr(cls, "vpp"):
1093 cls.lo_interfaces = []
1094 return cls.lo_interfaces
Klement Sekerab9ef2732018-06-24 22:49:33 +02001095 result = [VppLoInterface(cls) for i in range(count)]
1096 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +01001097 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +01001098 cls.lo_interfaces = result
1099 return result
1100
Neale Ranns192b13f2019-03-15 02:16:20 -07001101 @classmethod
1102 def create_bvi_interfaces(cls, count):
1103 """
1104 Create BVI interfaces.
1105
1106 :param count: number of interfaces created.
1107 :returns: List of created interfaces.
1108 """
Dave Wallace670724c2022-09-20 21:52:18 -04001109 if not hasattr(cls, "vpp"):
1110 cls.bvi_interfaces = []
1111 return cls.bvi_interfaces
Neale Ranns192b13f2019-03-15 02:16:20 -07001112 result = [VppBviInterface(cls) for i in range(count)]
1113 for intf in result:
1114 setattr(cls, intf.name, intf)
1115 cls.bvi_interfaces = result
1116 return result
1117
Damjan Marionf56b77a2016-10-03 19:44:57 +02001118 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001119 def extend_packet(packet, size, padding=" "):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001120 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001121 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +02001122 NOTE: Currently works only when Raw layer is present.
1123
1124 :param packet: packet
1125 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +02001126 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +02001127
1128 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001129 packet_len = len(packet) + 4
1130 extend = size - packet_len
1131 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +02001132 num = (extend // len(padding)) + 1
1133 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +02001134
Klement Sekeradab231a2016-12-21 08:50:14 +01001135 @classmethod
1136 def reset_packet_infos(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001137 """Reset the list of packet info objects and packet counts to zero"""
Klement Sekeradab231a2016-12-21 08:50:14 +01001138 cls._packet_infos = {}
1139 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +02001140
Klement Sekeradab231a2016-12-21 08:50:14 +01001141 @classmethod
1142 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001143 """
1144 Create packet info object containing the source and destination indexes
1145 and add it to the testcase's packet info list
1146
Klement Sekeradab231a2016-12-21 08:50:14 +01001147 :param VppInterface src_if: source interface
1148 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +02001149
1150 :returns: _PacketInfo object
1151
1152 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001153 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +01001154 info.index = len(cls._packet_infos)
1155 info.src = src_if.sw_if_index
1156 info.dst = dst_if.sw_if_index
1157 if isinstance(dst_if, VppSubInterface):
1158 dst_idx = dst_if.parent.sw_if_index
1159 else:
1160 dst_idx = dst_if.sw_if_index
1161 if dst_idx in cls._packet_count_for_dst_if_idx:
1162 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1163 else:
1164 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1165 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001166 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001167
Damjan Marionf56b77a2016-10-03 19:44:57 +02001168 @staticmethod
1169 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001170 """
1171 Convert _PacketInfo object to packet payload
1172
1173 :param info: _PacketInfo object
1174
1175 :returns: string containing serialized data from packet info
1176 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001177
1178 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001179 return pack("iiiih", info.index, info.src, info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001180
Damjan Marionf56b77a2016-10-03 19:44:57 +02001181 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001182 def payload_to_info(payload, payload_field="load"):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001183 """
1184 Convert packet payload to _PacketInfo object
1185
1186 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001187 :type payload: <class 'scapy.packet.Raw'>
1188 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001189 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001190 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001191 :returns: _PacketInfo object containing de-serialized data from payload
1192
1193 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001194
1195 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1196 payload_b = getattr(payload, payload_field)[:18]
1197
Damjan Marionf56b77a2016-10-03 19:44:57 +02001198 info = _PacketInfo()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001199 info.index, info.src, info.dst, info.ip, info.proto = unpack("iiiih", payload_b)
Ray Kinsellab8165b92021-09-22 11:24:06 +01001200
1201 # some SRv6 TCs depend on get an exception if bad values are detected
1202 if info.index > 0x4000:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001203 raise ValueError("Index value is invalid")
Ray Kinsellab8165b92021-09-22 11:24:06 +01001204
Damjan Marionf56b77a2016-10-03 19:44:57 +02001205 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001206
Damjan Marionf56b77a2016-10-03 19:44:57 +02001207 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001208 """
1209 Iterate over the packet info list stored in the testcase
1210 Start iteration with first element if info is None
1211 Continue based on index in info if info is specified
1212
1213 :param info: info or None
1214 :returns: next info in list or None if no more infos
1215 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001216 if info is None:
1217 next_index = 0
1218 else:
1219 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001220 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001221 return None
1222 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001223 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001224
Klement Sekeraf62ae122016-10-11 11:47:09 +02001225 def get_next_packet_info_for_interface(self, src_index, info):
1226 """
1227 Search the packet info list for the next packet info with same source
1228 interface index
1229
1230 :param src_index: source interface index to search for
1231 :param info: packet info - where to start the search
1232 :returns: packet info or None
1233
1234 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001235 while True:
1236 info = self.get_next_packet_info(info)
1237 if info is None:
1238 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001239 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001240 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001241
Klement Sekeraf62ae122016-10-11 11:47:09 +02001242 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1243 """
1244 Search the packet info list for the next packet info with same source
1245 and destination interface indexes
1246
1247 :param src_index: source interface index to search for
1248 :param dst_index: destination interface index to search for
1249 :param info: packet info - where to start the search
1250 :returns: packet info or None
1251
1252 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001253 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001254 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001255 if info is None:
1256 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001257 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001258 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001259
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001260 def assert_equal(self, real_value, expected_value, name_or_class=None):
1261 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001262 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001263 return
1264 try:
1265 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001266 msg = msg % (
1267 getdoc(name_or_class).strip(),
1268 real_value,
1269 str(name_or_class(real_value)),
1270 expected_value,
1271 str(name_or_class(expected_value)),
1272 )
Klement Sekera13a83ef2018-03-21 12:35:51 +01001273 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001274 msg = "Invalid %s: %s does not match expected value %s" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001275 name_or_class,
1276 real_value,
1277 expected_value,
1278 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001279
1280 self.assertEqual(real_value, expected_value, msg)
1281
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001282 def assert_in_range(self, real_value, expected_min, expected_max, name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001283 if name is None:
1284 msg = None
1285 else:
1286 msg = "Invalid %s: %s out of range <%s,%s>" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001287 name,
1288 real_value,
1289 expected_min,
1290 expected_max,
1291 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001292 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1293
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001294 def assert_packet_checksums_valid(self, packet, ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001295 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001296 udp_layers = ["UDP", "UDPerror"]
1297 checksum_fields = ["cksum", "chksum"]
Klement Sekerad81ae412018-05-16 10:52:54 +02001298 checksums = []
1299 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001300 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001301 while True:
1302 layer = temp.getlayer(counter)
1303 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001304 layer = layer.copy()
1305 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001306 for cf in checksum_fields:
1307 if hasattr(layer, cf):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001308 if (
1309 ignore_zero_udp_checksums
1310 and 0 == getattr(layer, cf)
1311 and layer.name in udp_layers
1312 ):
Klement Sekerad81ae412018-05-16 10:52:54 +02001313 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001314 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001315 checksums.append((counter, cf))
1316 else:
1317 break
1318 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001319 if 0 == len(checksums):
1320 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001321 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekera738cf732022-11-14 11:26:18 +01001322 for layer, cf in reversed(checksums):
Klement Sekera31da2e32018-06-24 22:49:55 +02001323 calc_sum = getattr(temp[layer], cf)
1324 self.assert_equal(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001325 getattr(received[layer], cf),
1326 calc_sum,
1327 "packet checksum on layer #%d: %s" % (layer, temp[layer].name),
1328 )
Klement Sekera31da2e32018-06-24 22:49:55 +02001329 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001330 "Checksum field `%s` on `%s` layer has correct value `%s`"
1331 % (cf, temp[layer].name, calc_sum)
1332 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001333
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001334 def assert_checksum_valid(
Klement Sekera738cf732022-11-14 11:26:18 +01001335 self,
1336 received_packet,
1337 layer,
1338 checksum_field_names=["chksum", "cksum"],
1339 ignore_zero_checksum=False,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001340 ):
1341 """Check checksum of received packet on given layer"""
Klement Sekera738cf732022-11-14 11:26:18 +01001342 layer_copy = received_packet[layer].copy()
1343 layer_copy.remove_payload()
1344 field_name = None
1345 for f in checksum_field_names:
1346 if hasattr(layer_copy, f):
1347 field_name = f
1348 break
1349 if field_name is None:
1350 raise Exception(
1351 f"Layer `{layer}` has none of checksum fields: `{checksum_field_names}`."
1352 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001353 received_packet_checksum = getattr(received_packet[layer], field_name)
1354 if ignore_zero_checksum and 0 == received_packet_checksum:
1355 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001356 recalculated = received_packet.__class__(scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001357 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001358 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001359 self.assert_equal(
1360 received_packet_checksum,
1361 getattr(recalculated[layer], field_name),
Klement Sekera738cf732022-11-14 11:26:18 +01001362 f"packet checksum (field: {field_name}) on layer: %s" % layer,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001363 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001364
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001365 def assert_ip_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1366 self.assert_checksum_valid(
1367 received_packet, "IP", ignore_zero_checksum=ignore_zero_checksum
1368 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001369
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001370 def assert_tcp_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1371 self.assert_checksum_valid(
1372 received_packet, "TCP", ignore_zero_checksum=ignore_zero_checksum
1373 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001374
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001375 def assert_udp_checksum_valid(self, received_packet, ignore_zero_checksum=True):
1376 self.assert_checksum_valid(
1377 received_packet, "UDP", ignore_zero_checksum=ignore_zero_checksum
1378 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001379
1380 def assert_embedded_icmp_checksum_valid(self, received_packet):
1381 if received_packet.haslayer(IPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001382 self.assert_checksum_valid(received_packet, "IPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001383 if received_packet.haslayer(TCPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001384 self.assert_checksum_valid(received_packet, "TCPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001385 if received_packet.haslayer(UDPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001386 self.assert_checksum_valid(
1387 received_packet, "UDPerror", ignore_zero_checksum=True
1388 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001389 if received_packet.haslayer(ICMPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001390 self.assert_checksum_valid(received_packet, "ICMPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001391
1392 def assert_icmp_checksum_valid(self, received_packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001393 self.assert_checksum_valid(received_packet, "ICMP")
Klement Sekerad81ae412018-05-16 10:52:54 +02001394 self.assert_embedded_icmp_checksum_valid(received_packet)
1395
1396 def assert_icmpv6_checksum_valid(self, pkt):
1397 if pkt.haslayer(ICMPv6DestUnreach):
Klement Sekera738cf732022-11-14 11:26:18 +01001398 self.assert_checksum_valid(pkt, "ICMPv6DestUnreach")
Klement Sekerad81ae412018-05-16 10:52:54 +02001399 self.assert_embedded_icmp_checksum_valid(pkt)
1400 if pkt.haslayer(ICMPv6EchoRequest):
Klement Sekera738cf732022-11-14 11:26:18 +01001401 self.assert_checksum_valid(pkt, "ICMPv6EchoRequest")
Klement Sekerad81ae412018-05-16 10:52:54 +02001402 if pkt.haslayer(ICMPv6EchoReply):
Klement Sekera738cf732022-11-14 11:26:18 +01001403 self.assert_checksum_valid(pkt, "ICMPv6EchoReply")
Klement Sekerad81ae412018-05-16 10:52:54 +02001404
Klement Sekera107ad732022-02-18 10:32:08 +00001405 def get_counter(self, counter):
Klement Sekera3a343d42019-05-16 14:35:46 +02001406 if counter.startswith("/"):
1407 counter_value = self.statistics.get_counter(counter)
1408 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001409 counters = self.vapi.cli("sh errors").split("\n")
Klement Sekera6aa58b72019-05-16 14:34:55 +02001410 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001411 for i in range(1, len(counters) - 1):
1412 results = counters[i].split()
1413 if results[1] == counter:
1414 counter_value = int(results[0])
1415 break
1416 return counter_value
1417
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001418 def assert_counter_equal(self, counter, expected_value, thread=None, index=0):
Klement Sekera107ad732022-02-18 10:32:08 +00001419 c = self.get_counter(counter)
1420 if thread is not None:
1421 c = c[thread][index]
1422 else:
1423 c = sum(x[index] for x in c)
1424 self.assert_equal(c, expected_value, "counter `%s'" % counter)
1425
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001426 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera107ad732022-02-18 10:32:08 +00001427 counter_value = self.get_counter(counter)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001428 self.assert_equal(
1429 counter_value, expected_value, "packet counter `%s'" % counter
1430 )
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001431
Ole Troan233e4682019-05-16 15:01:34 +02001432 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001433 counter_value = self.statistics[counter].sum()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001434 self.assert_equal(counter_value, expected_value, "error counter `%s'" % counter)
Ole Troan233e4682019-05-16 15:01:34 +02001435
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001436 @classmethod
1437 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001438 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1439 # * by Guido, only the main thread can be interrupted.
1440 # */
1441 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1442 if timeout == 0:
1443 # yield quantum
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001444 if hasattr(os, "sched_yield"):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001445 os.sched_yield()
1446 else:
1447 time.sleep(0)
1448 return
1449
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001450 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001451 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001452 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001453 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001454 if after - before > 2 * timeout:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001455 cls.logger.error(
1456 "unexpected self.sleep() result - slept for %es instead of ~%es!",
1457 after - before,
1458 timeout,
1459 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001460
1461 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001462 "Finished sleep (%s) - slept %es (wanted %es)",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001463 remark,
1464 after - before,
1465 timeout,
1466 )
Klement Sekeraa57a9702017-02-02 06:58:07 +01001467
Benoît Ganne56eccdb2021-08-20 09:18:31 +02001468 def virtual_sleep(self, timeout, remark=None):
1469 self.logger.debug("Moving VPP time by %s (%s)", timeout, remark)
1470 self.vapi.cli("set clock adjust %s" % timeout)
1471
Benoît Ganne8c45e512021-02-19 16:39:13 +01001472 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001473 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001474 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001475 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001476
Klement Sekeraad3187f2022-02-18 10:34:35 +00001477 def snapshot_stats(self, stats_diff):
1478 """Return snapshot of interesting stats based on diff dictionary."""
1479 stats_snapshot = {}
1480 for sw_if_index in stats_diff:
1481 for counter in stats_diff[sw_if_index]:
1482 stats_snapshot[counter] = self.statistics[counter]
1483 self.logger.debug(f"Took statistics stats_snapshot: {stats_snapshot}")
1484 return stats_snapshot
1485
1486 def compare_stats_with_snapshot(self, stats_diff, stats_snapshot):
1487 """Assert appropriate difference between current stats and snapshot."""
1488 for sw_if_index in stats_diff:
1489 for cntr, diff in stats_diff[sw_if_index].items():
1490 if sw_if_index == "err":
1491 self.assert_equal(
1492 self.statistics[cntr].sum(),
1493 stats_snapshot[cntr].sum() + diff,
1494 f"'{cntr}' counter value (previous value: "
1495 f"{stats_snapshot[cntr].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001496 f"expected diff: {diff})",
1497 )
Klement Sekeraad3187f2022-02-18 10:34:35 +00001498 else:
1499 try:
1500 self.assert_equal(
1501 self.statistics[cntr][:, sw_if_index].sum(),
1502 stats_snapshot[cntr][:, sw_if_index].sum() + diff,
1503 f"'{cntr}' counter value (previous value: "
1504 f"{stats_snapshot[cntr][:, sw_if_index].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001505 f"expected diff: {diff})",
1506 )
Klement Sekera01578852023-01-26 13:14:01 +01001507 except IndexError as e:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001508 # if diff is 0, then this most probably a case where
1509 # test declares multiple interfaces but traffic hasn't
1510 # passed through this one yet - which means the counter
1511 # value is 0 and can be ignored
1512 if 0 != diff:
Klement Sekera01578852023-01-26 13:14:01 +01001513 raise Exception(
1514 f"Couldn't sum counter: {cntr} on sw_if_index: {sw_if_index}"
1515 ) from e
Klement Sekeraad3187f2022-02-18 10:34:35 +00001516
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001517 def send_and_assert_no_replies(
1518 self, intf, pkts, remark="", timeout=None, stats_diff=None, trace=True, msg=None
1519 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001520 if stats_diff:
1521 stats_snapshot = self.snapshot_stats(stats_diff)
1522
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001523 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001524
1525 try:
1526 if not timeout:
1527 timeout = 1
1528 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +00001529 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001530 timeout = 0.1
1531 finally:
1532 if trace:
1533 if msg:
1534 self.logger.debug(f"send_and_assert_no_replies: {msg}")
1535 self.logger.debug(self.vapi.cli("show trace"))
1536
1537 if stats_diff:
1538 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -08001539
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001540 def send_and_expect(
1541 self,
1542 intf,
1543 pkts,
1544 output,
1545 n_rx=None,
1546 worker=None,
1547 trace=True,
1548 msg=None,
1549 stats_diff=None,
1550 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001551 if stats_diff:
1552 stats_snapshot = self.snapshot_stats(stats_diff)
1553
Neale Rannsd7603d92019-03-28 08:56:10 +00001554 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +00001555 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001556 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001557 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +02001558 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001559 if msg:
1560 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +02001561 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +00001562
1563 if stats_diff:
1564 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1565
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001566 return rx
1567
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001568 def send_and_expect_load_balancing(
1569 self, input, pkts, outputs, worker=None, trace=True
1570 ):
Neale Ranns699bea22022-02-17 09:22:16 +00001571 self.pg_send(input, pkts, worker=worker, trace=trace)
1572 rxs = []
1573 for oo in outputs:
1574 rx = oo._get_capture(1)
1575 self.assertNotEqual(0, len(rx))
1576 rxs.append(rx)
1577 if trace:
1578 self.logger.debug(self.vapi.cli("show trace"))
1579 return rxs
1580
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001581 def send_and_expect_some(self, intf, pkts, output, worker=None, trace=True):
Neale Ranns5c6dd172022-02-17 09:08:47 +00001582 self.pg_send(intf, pkts, worker=worker, trace=trace)
1583 rx = output._get_capture(1)
1584 if trace:
1585 self.logger.debug(self.vapi.cli("show trace"))
1586 self.assertTrue(len(rx) > 0)
1587 self.assertTrue(len(rx) < len(pkts))
1588 return rx
1589
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001590 def send_and_expect_only(self, intf, pkts, output, timeout=None, stats_diff=None):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001591 if stats_diff:
1592 stats_snapshot = self.snapshot_stats(stats_diff)
1593
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001594 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001595 rx = output.get_capture(len(pkts))
1596 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001597 if not timeout:
1598 timeout = 1
1599 for i in self.pg_interfaces:
1600 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +00001601 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001602 timeout = 0.1
1603
Klement Sekeraad3187f2022-02-18 10:34:35 +00001604 if stats_diff:
1605 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1606
Neale Ranns52fae862018-01-08 04:41:42 -08001607 return rx
1608
Damjan Marionf56b77a2016-10-03 19:44:57 +02001609
juraj.linkes184870a2018-07-16 14:22:01 +02001610def get_testcase_doc_name(test):
1611 return getdoc(test.__class__).splitlines()[0]
1612
1613
Ole Trøan5ba91592018-11-22 10:01:09 +00001614def get_test_description(descriptions, test):
1615 short_description = test.shortDescription()
1616 if descriptions and short_description:
1617 return short_description
1618 else:
1619 return str(test)
1620
1621
juraj.linkes40dd73b2018-09-21 13:55:16 +02001622class TestCaseInfo(object):
1623 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1624 self.logger = logger
1625 self.tempdir = tempdir
1626 self.vpp_pid = vpp_pid
1627 self.vpp_bin_path = vpp_bin_path
1628 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001629
1630
Damjan Marionf56b77a2016-10-03 19:44:57 +02001631class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001632 """
1633 @property result_string
1634 String variable to store the test case result string.
1635 @property errors
1636 List variable containing 2-tuples of TestCase instances and strings
1637 holding formatted tracebacks. Each tuple represents a test which
1638 raised an unexpected exception.
1639 @property failures
1640 List variable containing 2-tuples of TestCase instances and strings
1641 holding formatted tracebacks. Each tuple represents a test where
1642 a failure was explicitly signalled using the TestCase.assert*()
1643 methods.
1644 """
1645
juraj.linkes40dd73b2018-09-21 13:55:16 +02001646 failed_test_cases_info = set()
1647 core_crash_test_cases_info = set()
1648 current_test_case_info = None
1649
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001650 def __init__(self, stream=None, descriptions=None, verbosity=None, runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001651 """
Klement Sekerada505f62017-01-04 12:58:53 +01001652 :param stream File descriptor to store where to report test results.
1653 Set to the standard error stream by default.
1654 :param descriptions Boolean variable to store information if to use
1655 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001656 :param verbosity Integer variable to store required verbosity level.
1657 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001658 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001659 self.stream = stream
1660 self.descriptions = descriptions
1661 self.verbosity = verbosity
Klement Sekera47f35272023-03-29 16:04:58 +02001662 self.result_code = TestResultCode.TEST_RUN
Damjan Marionf56b77a2016-10-03 19:44:57 +02001663 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001664 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001665 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001666
Damjan Marionf56b77a2016-10-03 19:44:57 +02001667 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001668 """
1669 Record a test succeeded result
1670
1671 :param test:
1672
1673 """
Klement Sekera47f35272023-03-29 16:04:58 +02001674 self.log_result("addSuccess", test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001675 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001676 self.result_string = colorize("OK", GREEN)
Klement Sekera47f35272023-03-29 16:04:58 +02001677 self.result_code = TestResultCode.PASS
1678 self.send_result_through_pipe(test, self.result_code)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001679
Klement Sekera47f35272023-03-29 16:04:58 +02001680 def addExpectedFailure(self, test, err):
1681 self.log_result("addExpectedFailure", test, err)
1682 super().addExpectedFailure(test, err)
1683 self.result_string = colorize("FAIL", GREEN)
1684 self.result_code = TestResultCode.EXPECTED_FAIL
1685 self.send_result_through_pipe(test, self.result_code)
1686
1687 def addUnexpectedSuccess(self, test):
1688 self.log_result("addUnexpectedSuccess", test)
1689 super().addUnexpectedSuccess(test)
1690 self.result_string = colorize("OK", RED)
1691 self.result_code = TestResultCode.UNEXPECTED_PASS
1692 self.send_result_through_pipe(test, self.result_code)
juraj.linkescae64f82018-09-19 15:01:47 +02001693
Klement Sekeraf62ae122016-10-11 11:47:09 +02001694 def addSkip(self, test, reason):
1695 """
1696 Record a test skipped.
1697
1698 :param test:
1699 :param reason:
1700
1701 """
Klement Sekera47f35272023-03-29 16:04:58 +02001702 self.log_result("addSkip", test, reason=reason)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001703 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001704 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001705
Klement Sekera558ceab2021-04-08 19:37:41 +02001706 if reason == "not enough cpus":
Klement Sekera47f35272023-03-29 16:04:58 +02001707 self.result_code = TestResultCode.SKIP_CPU_SHORTAGE
Klement Sekera558ceab2021-04-08 19:37:41 +02001708 else:
Klement Sekera47f35272023-03-29 16:04:58 +02001709 self.result_code = TestResultCode.SKIP
1710 self.send_result_through_pipe(test, self.result_code)
juraj.linkescae64f82018-09-19 15:01:47 +02001711
juraj.linkes40dd73b2018-09-21 13:55:16 +02001712 def symlink_failed(self):
1713 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001714 try:
Klement Sekerab23ffd72021-05-31 16:08:53 +02001715 failed_dir = config.failed_dir
juraj.linkes40dd73b2018-09-21 13:55:16 +02001716 link_path = os.path.join(
1717 failed_dir,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001718 "%s-FAILED" % os.path.basename(self.current_test_case_info.tempdir),
1719 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001720
1721 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001722 "creating a link to the failed test"
1723 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001724 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001725 "os.symlink(%s, %s)"
1726 % (self.current_test_case_info.tempdir, link_path)
1727 )
juraj.linkes184870a2018-07-16 14:22:01 +02001728 if os.path.exists(link_path):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001729 self.current_test_case_info.logger.debug("symlink already exists")
juraj.linkes184870a2018-07-16 14:22:01 +02001730 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001731 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001732
Klement Sekeraf413bef2017-08-15 07:09:02 +02001733 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001734 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001735
juraj.linkescae64f82018-09-19 15:01:47 +02001736 def send_result_through_pipe(self, test, result):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001737 if hasattr(self, "test_framework_result_pipe"):
juraj.linkescae64f82018-09-19 15:01:47 +02001738 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001739 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001740 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001741
Klement Sekera47f35272023-03-29 16:04:58 +02001742 def log_result(self, fn, test, err=None, reason=None):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001743 if self.current_test_case_info:
1744 if isinstance(test, unittest.suite._ErrorHolder):
1745 test_name = test.description
1746 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001747 test_name = "%s.%s(%s)" % (
1748 test.__class__.__name__,
1749 test._testMethodName,
1750 test._testMethodDoc,
1751 )
Klement Sekera47f35272023-03-29 16:04:58 +02001752 extra_msg = ""
1753 if err:
1754 extra_msg += f", error is {err}"
1755 if reason:
1756 extra_msg += f", reason is {reason}"
juraj.linkes40dd73b2018-09-21 13:55:16 +02001757 self.current_test_case_info.logger.debug(
Klement Sekera47f35272023-03-29 16:04:58 +02001758 f"--- {fn}() {test_name} called{extra_msg}"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001759 )
Klement Sekera47f35272023-03-29 16:04:58 +02001760 if err:
1761 self.current_test_case_info.logger.debug(
1762 "formatted exception is:\n%s" % "".join(format_exception(*err))
1763 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001764
Klement Sekera47f35272023-03-29 16:04:58 +02001765 def add_error(self, test, err, unittest_fn, result_code):
1766 self.result_code = result_code
1767 if result_code == TestResultCode.FAIL:
1768 self.log_result("addFailure", test, err=err)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001769 error_type_str = colorize("FAIL", RED)
Klement Sekera47f35272023-03-29 16:04:58 +02001770 elif result_code == TestResultCode.ERROR:
1771 self.log_result("addError", test, err=err)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001772 error_type_str = colorize("ERROR", RED)
1773 else:
Klement Sekera47f35272023-03-29 16:04:58 +02001774 raise Exception(f"Unexpected result code {result_code}")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001775
1776 unittest_fn(self, test, err)
1777 if self.current_test_case_info:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001778 self.result_string = "%s [ temp dir used by test case: %s ]" % (
1779 error_type_str,
1780 self.current_test_case_info.tempdir,
1781 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001782 self.symlink_failed()
1783 self.failed_test_cases_info.add(self.current_test_case_info)
1784 if is_core_present(self.current_test_case_info.tempdir):
1785 if not self.current_test_case_info.core_crash_test:
1786 if isinstance(test, unittest.suite._ErrorHolder):
1787 test_name = str(test)
1788 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001789 test_name = "'{!s}' ({!s})".format(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001790 get_testcase_doc_name(test), test.id()
1791 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001792 self.current_test_case_info.core_crash_test = test_name
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001793 self.core_crash_test_cases_info.add(self.current_test_case_info)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001794 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001795 self.result_string = "%s [no temp dir]" % error_type_str
juraj.linkes40dd73b2018-09-21 13:55:16 +02001796
Klement Sekera47f35272023-03-29 16:04:58 +02001797 self.send_result_through_pipe(test, result_code)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001798
Damjan Marionf56b77a2016-10-03 19:44:57 +02001799 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001800 """
1801 Record a test failed result
1802
1803 :param test:
1804 :param err: error message
1805
1806 """
Klement Sekera47f35272023-03-29 16:04:58 +02001807 self.add_error(test, err, unittest.TestResult.addFailure, TestResultCode.FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001808
Damjan Marionf56b77a2016-10-03 19:44:57 +02001809 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001810 """
1811 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001812
Klement Sekeraf62ae122016-10-11 11:47:09 +02001813 :param test:
1814 :param err: error message
1815
1816 """
Klement Sekera47f35272023-03-29 16:04:58 +02001817 self.add_error(test, err, unittest.TestResult.addError, TestResultCode.ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001818
Damjan Marionf56b77a2016-10-03 19:44:57 +02001819 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001820 """
1821 Get test description
1822
1823 :param test:
1824 :returns: test description
1825
1826 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001827 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001828
Damjan Marionf56b77a2016-10-03 19:44:57 +02001829 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001830 """
1831 Start a test
1832
1833 :param test:
1834
1835 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001836
1837 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001838 if test.__class__ in self.printed:
1839 return
1840
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001841 test_doc = getdoc(test)
1842 if not test_doc:
1843 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001844
Klement Sekeraea6236b2021-03-25 14:03:44 +01001845 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001846 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001847 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001848 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001849
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001850 # This block may overwrite the colorized title above,
1851 # but we want this to stand out and be fixed
1852 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001853 test_title = colorize(f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001854
Naveen Joy6eaeea92021-09-09 17:57:02 -07001855 if test.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001856 test_title = colorize(f"FIXME with ASAN: {test_title}", RED)
Naveen Joy6eaeea92021-09-09 17:57:02 -07001857 test.skip_fixme_asan()
1858
Dave Wallacee95b2462022-09-18 22:28:44 -04001859 if is_distro_ubuntu2204 == True and test.has_tag(
1860 TestCaseTag.FIXME_UBUNTU2204
1861 ):
1862 test_title = colorize(f"FIXME on Ubuntu-22.04: {test_title}", RED)
1863 test.skip_fixme_ubuntu2204()
1864
Dave Wallace670724c2022-09-20 21:52:18 -04001865 if is_distro_debian11 == True and test.has_tag(TestCaseTag.FIXME_DEBIAN11):
1866 test_title = colorize(f"FIXME on Debian-11: {test_title}", RED)
1867 test.skip_fixme_debian11()
1868
Dave Wallace8a0a9d22022-10-04 22:02:49 -04001869 if "debug" in config.vpp_tag and test.has_tag(TestCaseTag.FIXME_VPP_DEBUG):
1870 test_title = colorize(f"FIXME on VPP Debug: {test_title}", RED)
1871 test.skip_fixme_vpp_debug()
1872
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001873 if hasattr(test, "vpp_worker_count"):
Klement Sekeraea6236b2021-03-25 14:03:44 +01001874 if test.vpp_worker_count == 0:
1875 test_title += " [main thread only]"
1876 elif test.vpp_worker_count == 1:
1877 test_title += " [1 worker thread]"
1878 else:
1879 test_title += f" [{test.vpp_worker_count} worker threads]"
1880
Klement Sekera558ceab2021-04-08 19:37:41 +02001881 if test.__class__.skipped_due_to_cpu_lack:
1882 test_title = colorize(
1883 f"{test_title} [skipped - not enough cpus, "
1884 f"required={test.__class__.get_cpus_required()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001885 f"available={max_vpp_cpus}]",
1886 YELLOW,
1887 )
Klement Sekera558ceab2021-04-08 19:37:41 +02001888
1889 print(double_line_delim)
1890 print(test_title)
1891 print(double_line_delim)
1892 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001893
1894 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001895 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001896 unittest.TestResult.startTest(self, test)
1897 if self.verbosity > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001898 self.stream.writeln("Starting " + self.getDescription(test) + " ...")
Klement Sekeraf62ae122016-10-11 11:47:09 +02001899 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001900
Damjan Marionf56b77a2016-10-03 19:44:57 +02001901 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001902 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001903 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001904
1905 :param test:
1906
1907 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001908 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001909
Klement Sekera47f35272023-03-29 16:04:58 +02001910 result_code_to_suffix = {
1911 TestResultCode.PASS: "",
1912 TestResultCode.FAIL: "",
1913 TestResultCode.ERROR: "",
1914 TestResultCode.SKIP: "",
1915 TestResultCode.TEST_RUN: "",
1916 TestResultCode.SKIP_CPU_SHORTAGE: "",
1917 TestResultCode.EXPECTED_FAIL: " [EXPECTED FAIL]",
1918 TestResultCode.UNEXPECTED_PASS: " [UNEXPECTED PASS]",
1919 }
1920
Damjan Marionf56b77a2016-10-03 19:44:57 +02001921 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001922 self.stream.writeln(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001923 self.stream.writeln(
Klement Sekera47f35272023-03-29 16:04:58 +02001924 "%-72s%s%s"
1925 % (
1926 self.getDescription(test),
1927 self.result_string,
1928 result_code_to_suffix[self.result_code],
1929 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001930 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001931 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001932 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001933 self.stream.writeln(
Klement Sekera47f35272023-03-29 16:04:58 +02001934 "%-67s %4.2f %s%s"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001935 % (
1936 self.getDescription(test),
1937 time.time() - self.start_test,
1938 self.result_string,
Klement Sekera47f35272023-03-29 16:04:58 +02001939 result_code_to_suffix[self.result_code],
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001940 )
1941 )
juraj.linkescae64f82018-09-19 15:01:47 +02001942
Klement Sekera47f35272023-03-29 16:04:58 +02001943 self.send_result_through_pipe(test, TestResultCode.TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001944
Damjan Marionf56b77a2016-10-03 19:44:57 +02001945 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001946 """
1947 Print errors from running the test case
1948 """
juraj.linkesabec0122018-11-16 17:28:56 +01001949 if len(self.errors) > 0 or len(self.failures) > 0:
1950 self.stream.writeln()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001951 self.printErrorList("ERROR", self.errors)
1952 self.printErrorList("FAIL", self.failures)
juraj.linkesabec0122018-11-16 17:28:56 +01001953
1954 # ^^ that is the last output from unittest before summary
1955 if not self.runner.print_summary:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001956 devnull = unittest.runner._WritelnDecorator(open(os.devnull, "w"))
juraj.linkesabec0122018-11-16 17:28:56 +01001957 self.stream = devnull
1958 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001959
Damjan Marionf56b77a2016-10-03 19:44:57 +02001960 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001961 """
1962 Print error list to the output stream together with error type
1963 and test case description.
1964
1965 :param flavour: error type
1966 :param errors: iterable errors
1967
1968 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001969 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001970 self.stream.writeln(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001971 self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001972 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001973 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001974
1975
Damjan Marionf56b77a2016-10-03 19:44:57 +02001976class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001977 """
Klement Sekera104543f2017-02-03 07:29:43 +01001978 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001979 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001980
Klement Sekeraf62ae122016-10-11 11:47:09 +02001981 @property
1982 def resultclass(self):
1983 """Class maintaining the results of the tests"""
1984 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001985
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001986 def __init__(
1987 self,
1988 keep_alive_pipe=None,
1989 descriptions=True,
1990 verbosity=1,
1991 result_pipe=None,
1992 failfast=False,
1993 buffer=False,
1994 resultclass=None,
1995 print_summary=True,
1996 **kwargs,
1997 ):
Klement Sekera7a161da2017-01-17 13:42:48 +01001998 # ignore stream setting here, use hard-coded stdout to be in sync
1999 # with prints from VppTestCase methods ...
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002000 super(VppTestRunner, self).__init__(
2001 sys.stdout, descriptions, verbosity, failfast, buffer, resultclass, **kwargs
2002 )
juraj.linkesccfead62018-11-21 13:20:43 +01002003 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01002004
juraj.linkesabec0122018-11-16 17:28:56 +01002005 self.orig_stream = self.stream
2006 self.resultclass.test_framework_result_pipe = result_pipe
2007
2008 self.print_summary = print_summary
2009
2010 def _makeResult(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002011 return self.resultclass(self.stream, self.descriptions, self.verbosity, self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02002012
Damjan Marionf56b77a2016-10-03 19:44:57 +02002013 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02002014 """
2015 Run the tests
2016
2017 :param test:
2018
2019 """
Klement Sekera3658adc2017-06-07 08:19:47 +02002020 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02002021
2022 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01002023 if not self.print_summary:
2024 self.stream = self.orig_stream
2025 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02002026 return result
Neale Ranns812ed392017-10-16 04:20:13 -07002027
2028
2029class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002030 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
2031 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07002032 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002033 self.args = executable_args
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002034 if hasattr(self, "testcase") and self.testcase.debug_all:
Dave Wallace24564332019-10-21 02:53:14 +00002035 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002036 self.args = [
2037 "/usr/bin/gdbserver",
2038 "localhost:{port}".format(port=self.testcase.gdbserver_port),
2039 ] + args
2040 elif self.testcase.debug_gdb and hasattr(self, "wait_for_gdb"):
Dave Wallace24564332019-10-21 02:53:14 +00002041 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002042 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00002043 self.app_name = os.path.basename(self.app_bin)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002044 if hasattr(self, "role"):
2045 self.app_name += " {role}".format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002046 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07002047 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002048 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05002049 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07002050
Dave Wallace24564332019-10-21 02:53:14 +00002051 def wait_for_enter(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002052 if not hasattr(self, "testcase"):
Dave Wallace24564332019-10-21 02:53:14 +00002053 return
2054 if self.testcase.debug_all and self.testcase.debug_gdbserver:
2055 print()
2056 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002057 print(
2058 "Spawned GDB Server for '{app}' with PID: {pid}".format(
2059 app=self.app_name, pid=self.process.pid
2060 )
2061 )
Dave Wallace24564332019-10-21 02:53:14 +00002062 elif self.testcase.debug_all and self.testcase.debug_gdb:
2063 print()
2064 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002065 print(
2066 "Spawned '{app}' with PID: {pid}".format(
2067 app=self.app_name, pid=self.process.pid
2068 )
2069 )
Dave Wallace24564332019-10-21 02:53:14 +00002070 else:
2071 return
2072 print(single_line_delim)
2073 print("You can debug '{app}' using:".format(app=self.app_name))
2074 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002075 print(
2076 "sudo gdb "
2077 + self.app_bin
2078 + " -ex 'target remote localhost:{port}'".format(
2079 port=self.testcase.gdbserver_port
2080 )
2081 )
2082 print(
2083 "Now is the time to attach gdb by running the above "
2084 "command, set up breakpoints etc., then resume from "
2085 "within gdb by issuing the 'continue' command"
2086 )
Dave Wallace24564332019-10-21 02:53:14 +00002087 self.testcase.gdbserver_port += 1
2088 elif self.testcase.debug_gdb:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002089 print(
2090 "sudo gdb "
2091 + self.app_bin
2092 + " -ex 'attach {pid}'".format(pid=self.process.pid)
2093 )
2094 print(
2095 "Now is the time to attach gdb by running the above "
2096 "command and set up breakpoints etc., then resume from"
2097 " within gdb by issuing the 'continue' command"
2098 )
Dave Wallace24564332019-10-21 02:53:14 +00002099 print(single_line_delim)
2100 input("Press ENTER to continue running the testcase...")
2101
Neale Ranns812ed392017-10-16 04:20:13 -07002102 def run(self):
2103 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002104 if not os.path.exists(executable) or not os.access(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002105 executable, os.F_OK | os.X_OK
2106 ):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002107 # Exit code that means some system file did not exist,
2108 # could not be opened, or had some other kind of error.
2109 self.result = os.EX_OSFILE
2110 raise EnvironmentError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002111 "executable '%s' is not found or executable." % executable
2112 )
2113 self.logger.debug(
2114 "Running executable '{app}': '{cmd}'".format(
2115 app=self.app_name, cmd=" ".join(self.args)
2116 )
2117 )
Neale Ranns812ed392017-10-16 04:20:13 -07002118 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05002119 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07002120 env["CK_LOG_FILE_NAME"] = "-"
2121 self.process = subprocess.Popen(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002122 ["stdbuf", "-o0", "-e0"] + self.args,
2123 shell=False,
2124 env=env,
2125 preexec_fn=os.setpgrp,
2126 stdout=subprocess.PIPE,
2127 stderr=subprocess.PIPE,
2128 )
Dave Wallace24564332019-10-21 02:53:14 +00002129 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07002130 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00002131 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07002132 self.logger.info("Return code is `%s'" % self.process.returncode)
2133 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002134 self.logger.info(
2135 "Executable `{app}' wrote to stdout:".format(app=self.app_name)
2136 )
Neale Ranns812ed392017-10-16 04:20:13 -07002137 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002138 self.logger.info(out.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002139 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002140 self.logger.info(
2141 "Executable `{app}' wrote to stderr:".format(app=self.app_name)
2142 )
Neale Ranns812ed392017-10-16 04:20:13 -07002143 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002144 self.logger.info(err.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002145 self.logger.info(single_line_delim)
2146 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002147
Klement Sekera6aa58b72019-05-16 14:34:55 +02002148
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002149if __name__ == "__main__":
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002150 pass