blob: cd39e1b311c6674d40e0ec7db0510fe075d6e81b [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"""
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001005 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
1439 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1440 # * by Guido, only the main thread can be interrupted.
1441 # */
1442 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1443 if timeout == 0:
1444 # yield quantum
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001445 if hasattr(os, "sched_yield"):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001446 os.sched_yield()
1447 else:
1448 time.sleep(0)
1449 return
1450
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001451 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001452 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001453 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001454 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001455 if after - before > 2 * timeout:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001456 cls.logger.error(
1457 "unexpected self.sleep() result - slept for %es instead of ~%es!",
1458 after - before,
1459 timeout,
1460 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001461
1462 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001463 "Finished sleep (%s) - slept %es (wanted %es)",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001464 remark,
1465 after - before,
1466 timeout,
1467 )
Klement Sekeraa57a9702017-02-02 06:58:07 +01001468
Benoît Ganne56eccdb2021-08-20 09:18:31 +02001469 def virtual_sleep(self, timeout, remark=None):
1470 self.logger.debug("Moving VPP time by %s (%s)", timeout, remark)
1471 self.vapi.cli("set clock adjust %s" % timeout)
1472
Benoît Ganne8c45e512021-02-19 16:39:13 +01001473 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001474 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001475 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001476 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001477
Klement Sekeraad3187f2022-02-18 10:34:35 +00001478 def snapshot_stats(self, stats_diff):
1479 """Return snapshot of interesting stats based on diff dictionary."""
1480 stats_snapshot = {}
1481 for sw_if_index in stats_diff:
1482 for counter in stats_diff[sw_if_index]:
1483 stats_snapshot[counter] = self.statistics[counter]
1484 self.logger.debug(f"Took statistics stats_snapshot: {stats_snapshot}")
1485 return stats_snapshot
1486
1487 def compare_stats_with_snapshot(self, stats_diff, stats_snapshot):
1488 """Assert appropriate difference between current stats and snapshot."""
1489 for sw_if_index in stats_diff:
1490 for cntr, diff in stats_diff[sw_if_index].items():
1491 if sw_if_index == "err":
1492 self.assert_equal(
1493 self.statistics[cntr].sum(),
1494 stats_snapshot[cntr].sum() + diff,
1495 f"'{cntr}' counter value (previous value: "
1496 f"{stats_snapshot[cntr].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001497 f"expected diff: {diff})",
1498 )
Klement Sekeraad3187f2022-02-18 10:34:35 +00001499 else:
1500 try:
1501 self.assert_equal(
1502 self.statistics[cntr][:, sw_if_index].sum(),
1503 stats_snapshot[cntr][:, sw_if_index].sum() + diff,
1504 f"'{cntr}' counter value (previous value: "
1505 f"{stats_snapshot[cntr][:, sw_if_index].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001506 f"expected diff: {diff})",
1507 )
Klement Sekera01578852023-01-26 13:14:01 +01001508 except IndexError as e:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001509 # if diff is 0, then this most probably a case where
1510 # test declares multiple interfaces but traffic hasn't
1511 # passed through this one yet - which means the counter
1512 # value is 0 and can be ignored
1513 if 0 != diff:
Klement Sekera01578852023-01-26 13:14:01 +01001514 raise Exception(
1515 f"Couldn't sum counter: {cntr} on sw_if_index: {sw_if_index}"
1516 ) from e
Klement Sekeraad3187f2022-02-18 10:34:35 +00001517
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001518 def send_and_assert_no_replies(
1519 self, intf, pkts, remark="", timeout=None, stats_diff=None, trace=True, msg=None
1520 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001521 if stats_diff:
1522 stats_snapshot = self.snapshot_stats(stats_diff)
1523
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001524 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001525
1526 try:
1527 if not timeout:
1528 timeout = 1
1529 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +00001530 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001531 timeout = 0.1
1532 finally:
1533 if trace:
1534 if msg:
1535 self.logger.debug(f"send_and_assert_no_replies: {msg}")
1536 self.logger.debug(self.vapi.cli("show trace"))
1537
1538 if stats_diff:
1539 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -08001540
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001541 def send_and_expect(
1542 self,
1543 intf,
1544 pkts,
1545 output,
1546 n_rx=None,
1547 worker=None,
1548 trace=True,
1549 msg=None,
1550 stats_diff=None,
1551 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001552 if stats_diff:
1553 stats_snapshot = self.snapshot_stats(stats_diff)
1554
Neale Rannsd7603d92019-03-28 08:56:10 +00001555 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +00001556 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001557 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001558 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +02001559 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001560 if msg:
1561 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +02001562 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +00001563
1564 if stats_diff:
1565 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1566
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001567 return rx
1568
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001569 def send_and_expect_load_balancing(
1570 self, input, pkts, outputs, worker=None, trace=True
1571 ):
Neale Ranns699bea22022-02-17 09:22:16 +00001572 self.pg_send(input, pkts, worker=worker, trace=trace)
1573 rxs = []
1574 for oo in outputs:
1575 rx = oo._get_capture(1)
1576 self.assertNotEqual(0, len(rx))
1577 rxs.append(rx)
1578 if trace:
1579 self.logger.debug(self.vapi.cli("show trace"))
1580 return rxs
1581
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001582 def send_and_expect_some(self, intf, pkts, output, worker=None, trace=True):
Neale Ranns5c6dd172022-02-17 09:08:47 +00001583 self.pg_send(intf, pkts, worker=worker, trace=trace)
1584 rx = output._get_capture(1)
1585 if trace:
1586 self.logger.debug(self.vapi.cli("show trace"))
1587 self.assertTrue(len(rx) > 0)
1588 self.assertTrue(len(rx) < len(pkts))
1589 return rx
1590
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001591 def send_and_expect_only(self, intf, pkts, output, timeout=None, stats_diff=None):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001592 if stats_diff:
1593 stats_snapshot = self.snapshot_stats(stats_diff)
1594
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001595 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001596 rx = output.get_capture(len(pkts))
1597 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001598 if not timeout:
1599 timeout = 1
1600 for i in self.pg_interfaces:
1601 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +00001602 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001603 timeout = 0.1
1604
Klement Sekeraad3187f2022-02-18 10:34:35 +00001605 if stats_diff:
1606 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1607
Neale Ranns52fae862018-01-08 04:41:42 -08001608 return rx
1609
Damjan Marionf56b77a2016-10-03 19:44:57 +02001610
juraj.linkes184870a2018-07-16 14:22:01 +02001611def get_testcase_doc_name(test):
1612 return getdoc(test.__class__).splitlines()[0]
1613
1614
Ole Trøan5ba91592018-11-22 10:01:09 +00001615def get_test_description(descriptions, test):
1616 short_description = test.shortDescription()
1617 if descriptions and short_description:
1618 return short_description
1619 else:
1620 return str(test)
1621
1622
juraj.linkes40dd73b2018-09-21 13:55:16 +02001623class TestCaseInfo(object):
1624 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1625 self.logger = logger
1626 self.tempdir = tempdir
1627 self.vpp_pid = vpp_pid
1628 self.vpp_bin_path = vpp_bin_path
1629 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001630
1631
Damjan Marionf56b77a2016-10-03 19:44:57 +02001632class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001633 """
1634 @property result_string
1635 String variable to store the test case result string.
1636 @property errors
1637 List variable containing 2-tuples of TestCase instances and strings
1638 holding formatted tracebacks. Each tuple represents a test which
1639 raised an unexpected exception.
1640 @property failures
1641 List variable containing 2-tuples of TestCase instances and strings
1642 holding formatted tracebacks. Each tuple represents a test where
1643 a failure was explicitly signalled using the TestCase.assert*()
1644 methods.
1645 """
1646
juraj.linkes40dd73b2018-09-21 13:55:16 +02001647 failed_test_cases_info = set()
1648 core_crash_test_cases_info = set()
1649 current_test_case_info = None
1650
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001651 def __init__(self, stream=None, descriptions=None, verbosity=None, runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001652 """
Klement Sekerada505f62017-01-04 12:58:53 +01001653 :param stream File descriptor to store where to report test results.
1654 Set to the standard error stream by default.
1655 :param descriptions Boolean variable to store information if to use
1656 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001657 :param verbosity Integer variable to store required verbosity level.
1658 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001659 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001660 self.stream = stream
1661 self.descriptions = descriptions
1662 self.verbosity = verbosity
Klement Sekera47f35272023-03-29 16:04:58 +02001663 self.result_code = TestResultCode.TEST_RUN
Damjan Marionf56b77a2016-10-03 19:44:57 +02001664 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001665 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001666 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001667
Damjan Marionf56b77a2016-10-03 19:44:57 +02001668 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001669 """
1670 Record a test succeeded result
1671
1672 :param test:
1673
1674 """
Klement Sekera47f35272023-03-29 16:04:58 +02001675 self.log_result("addSuccess", test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001676 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001677 self.result_string = colorize("OK", GREEN)
Klement Sekera47f35272023-03-29 16:04:58 +02001678 self.result_code = TestResultCode.PASS
1679 self.send_result_through_pipe(test, self.result_code)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001680
Klement Sekera47f35272023-03-29 16:04:58 +02001681 def addExpectedFailure(self, test, err):
1682 self.log_result("addExpectedFailure", test, err)
1683 super().addExpectedFailure(test, err)
1684 self.result_string = colorize("FAIL", GREEN)
1685 self.result_code = TestResultCode.EXPECTED_FAIL
1686 self.send_result_through_pipe(test, self.result_code)
1687
1688 def addUnexpectedSuccess(self, test):
1689 self.log_result("addUnexpectedSuccess", test)
1690 super().addUnexpectedSuccess(test)
1691 self.result_string = colorize("OK", RED)
1692 self.result_code = TestResultCode.UNEXPECTED_PASS
1693 self.send_result_through_pipe(test, self.result_code)
juraj.linkescae64f82018-09-19 15:01:47 +02001694
Klement Sekeraf62ae122016-10-11 11:47:09 +02001695 def addSkip(self, test, reason):
1696 """
1697 Record a test skipped.
1698
1699 :param test:
1700 :param reason:
1701
1702 """
Klement Sekera47f35272023-03-29 16:04:58 +02001703 self.log_result("addSkip", test, reason=reason)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001704 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001705 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001706
Klement Sekera558ceab2021-04-08 19:37:41 +02001707 if reason == "not enough cpus":
Klement Sekera47f35272023-03-29 16:04:58 +02001708 self.result_code = TestResultCode.SKIP_CPU_SHORTAGE
Klement Sekera558ceab2021-04-08 19:37:41 +02001709 else:
Klement Sekera47f35272023-03-29 16:04:58 +02001710 self.result_code = TestResultCode.SKIP
1711 self.send_result_through_pipe(test, self.result_code)
juraj.linkescae64f82018-09-19 15:01:47 +02001712
juraj.linkes40dd73b2018-09-21 13:55:16 +02001713 def symlink_failed(self):
1714 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001715 try:
Klement Sekerab23ffd72021-05-31 16:08:53 +02001716 failed_dir = config.failed_dir
juraj.linkes40dd73b2018-09-21 13:55:16 +02001717 link_path = os.path.join(
1718 failed_dir,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001719 "%s-FAILED" % os.path.basename(self.current_test_case_info.tempdir),
1720 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001721
1722 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001723 "creating a link to the failed test"
1724 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001725 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001726 "os.symlink(%s, %s)"
1727 % (self.current_test_case_info.tempdir, link_path)
1728 )
juraj.linkes184870a2018-07-16 14:22:01 +02001729 if os.path.exists(link_path):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001730 self.current_test_case_info.logger.debug("symlink already exists")
juraj.linkes184870a2018-07-16 14:22:01 +02001731 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001732 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001733
Klement Sekeraf413bef2017-08-15 07:09:02 +02001734 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001735 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001736
juraj.linkescae64f82018-09-19 15:01:47 +02001737 def send_result_through_pipe(self, test, result):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001738 if hasattr(self, "test_framework_result_pipe"):
juraj.linkescae64f82018-09-19 15:01:47 +02001739 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001740 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001741 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001742
Klement Sekera47f35272023-03-29 16:04:58 +02001743 def log_result(self, fn, test, err=None, reason=None):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001744 if self.current_test_case_info:
1745 if isinstance(test, unittest.suite._ErrorHolder):
1746 test_name = test.description
1747 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001748 test_name = "%s.%s(%s)" % (
1749 test.__class__.__name__,
1750 test._testMethodName,
1751 test._testMethodDoc,
1752 )
Klement Sekera47f35272023-03-29 16:04:58 +02001753 extra_msg = ""
1754 if err:
1755 extra_msg += f", error is {err}"
1756 if reason:
1757 extra_msg += f", reason is {reason}"
juraj.linkes40dd73b2018-09-21 13:55:16 +02001758 self.current_test_case_info.logger.debug(
Klement Sekera47f35272023-03-29 16:04:58 +02001759 f"--- {fn}() {test_name} called{extra_msg}"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001760 )
Klement Sekera47f35272023-03-29 16:04:58 +02001761 if err:
1762 self.current_test_case_info.logger.debug(
1763 "formatted exception is:\n%s" % "".join(format_exception(*err))
1764 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001765
Klement Sekera47f35272023-03-29 16:04:58 +02001766 def add_error(self, test, err, unittest_fn, result_code):
1767 self.result_code = result_code
1768 if result_code == TestResultCode.FAIL:
1769 self.log_result("addFailure", test, err=err)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001770 error_type_str = colorize("FAIL", RED)
Klement Sekera47f35272023-03-29 16:04:58 +02001771 elif result_code == TestResultCode.ERROR:
1772 self.log_result("addError", test, err=err)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001773 error_type_str = colorize("ERROR", RED)
1774 else:
Klement Sekera47f35272023-03-29 16:04:58 +02001775 raise Exception(f"Unexpected result code {result_code}")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001776
1777 unittest_fn(self, test, err)
1778 if self.current_test_case_info:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001779 self.result_string = "%s [ temp dir used by test case: %s ]" % (
1780 error_type_str,
1781 self.current_test_case_info.tempdir,
1782 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001783 self.symlink_failed()
1784 self.failed_test_cases_info.add(self.current_test_case_info)
1785 if is_core_present(self.current_test_case_info.tempdir):
1786 if not self.current_test_case_info.core_crash_test:
1787 if isinstance(test, unittest.suite._ErrorHolder):
1788 test_name = str(test)
1789 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001790 test_name = "'{!s}' ({!s})".format(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001791 get_testcase_doc_name(test), test.id()
1792 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001793 self.current_test_case_info.core_crash_test = test_name
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001794 self.core_crash_test_cases_info.add(self.current_test_case_info)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001795 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001796 self.result_string = "%s [no temp dir]" % error_type_str
juraj.linkes40dd73b2018-09-21 13:55:16 +02001797
Klement Sekera47f35272023-03-29 16:04:58 +02001798 self.send_result_through_pipe(test, result_code)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001799
Damjan Marionf56b77a2016-10-03 19:44:57 +02001800 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001801 """
1802 Record a test failed result
1803
1804 :param test:
1805 :param err: error message
1806
1807 """
Klement Sekera47f35272023-03-29 16:04:58 +02001808 self.add_error(test, err, unittest.TestResult.addFailure, TestResultCode.FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001809
Damjan Marionf56b77a2016-10-03 19:44:57 +02001810 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001811 """
1812 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001813
Klement Sekeraf62ae122016-10-11 11:47:09 +02001814 :param test:
1815 :param err: error message
1816
1817 """
Klement Sekera47f35272023-03-29 16:04:58 +02001818 self.add_error(test, err, unittest.TestResult.addError, TestResultCode.ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001819
Damjan Marionf56b77a2016-10-03 19:44:57 +02001820 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001821 """
1822 Get test description
1823
1824 :param test:
1825 :returns: test description
1826
1827 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001828 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001829
Damjan Marionf56b77a2016-10-03 19:44:57 +02001830 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001831 """
1832 Start a test
1833
1834 :param test:
1835
1836 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001837
1838 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001839 if test.__class__ in self.printed:
1840 return
1841
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001842 test_doc = getdoc(test)
1843 if not test_doc:
1844 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001845
Klement Sekeraea6236b2021-03-25 14:03:44 +01001846 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001847 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001848 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001849 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001850
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001851 # This block may overwrite the colorized title above,
1852 # but we want this to stand out and be fixed
1853 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001854 test_title = colorize(f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001855
Naveen Joy6eaeea92021-09-09 17:57:02 -07001856 if test.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001857 test_title = colorize(f"FIXME with ASAN: {test_title}", RED)
Naveen Joy6eaeea92021-09-09 17:57:02 -07001858 test.skip_fixme_asan()
1859
Dave Wallacee95b2462022-09-18 22:28:44 -04001860 if is_distro_ubuntu2204 == True and test.has_tag(
1861 TestCaseTag.FIXME_UBUNTU2204
1862 ):
1863 test_title = colorize(f"FIXME on Ubuntu-22.04: {test_title}", RED)
1864 test.skip_fixme_ubuntu2204()
1865
Dave Wallace670724c2022-09-20 21:52:18 -04001866 if is_distro_debian11 == True and test.has_tag(TestCaseTag.FIXME_DEBIAN11):
1867 test_title = colorize(f"FIXME on Debian-11: {test_title}", RED)
1868 test.skip_fixme_debian11()
1869
Dave Wallace8a0a9d22022-10-04 22:02:49 -04001870 if "debug" in config.vpp_tag and test.has_tag(TestCaseTag.FIXME_VPP_DEBUG):
1871 test_title = colorize(f"FIXME on VPP Debug: {test_title}", RED)
1872 test.skip_fixme_vpp_debug()
1873
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001874 if hasattr(test, "vpp_worker_count"):
Klement Sekeraea6236b2021-03-25 14:03:44 +01001875 if test.vpp_worker_count == 0:
1876 test_title += " [main thread only]"
1877 elif test.vpp_worker_count == 1:
1878 test_title += " [1 worker thread]"
1879 else:
1880 test_title += f" [{test.vpp_worker_count} worker threads]"
1881
Klement Sekera558ceab2021-04-08 19:37:41 +02001882 if test.__class__.skipped_due_to_cpu_lack:
1883 test_title = colorize(
1884 f"{test_title} [skipped - not enough cpus, "
1885 f"required={test.__class__.get_cpus_required()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001886 f"available={max_vpp_cpus}]",
1887 YELLOW,
1888 )
Klement Sekera558ceab2021-04-08 19:37:41 +02001889
1890 print(double_line_delim)
1891 print(test_title)
1892 print(double_line_delim)
1893 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001894
1895 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001896 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001897 unittest.TestResult.startTest(self, test)
1898 if self.verbosity > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001899 self.stream.writeln("Starting " + self.getDescription(test) + " ...")
Klement Sekeraf62ae122016-10-11 11:47:09 +02001900 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001901
Damjan Marionf56b77a2016-10-03 19:44:57 +02001902 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001903 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001904 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001905
1906 :param test:
1907
1908 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001909 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001910
Klement Sekera47f35272023-03-29 16:04:58 +02001911 result_code_to_suffix = {
1912 TestResultCode.PASS: "",
1913 TestResultCode.FAIL: "",
1914 TestResultCode.ERROR: "",
1915 TestResultCode.SKIP: "",
1916 TestResultCode.TEST_RUN: "",
1917 TestResultCode.SKIP_CPU_SHORTAGE: "",
1918 TestResultCode.EXPECTED_FAIL: " [EXPECTED FAIL]",
1919 TestResultCode.UNEXPECTED_PASS: " [UNEXPECTED PASS]",
1920 }
1921
Damjan Marionf56b77a2016-10-03 19:44:57 +02001922 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001923 self.stream.writeln(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001924 self.stream.writeln(
Klement Sekera47f35272023-03-29 16:04:58 +02001925 "%-72s%s%s"
1926 % (
1927 self.getDescription(test),
1928 self.result_string,
1929 result_code_to_suffix[self.result_code],
1930 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001931 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001932 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001933 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001934 self.stream.writeln(
Klement Sekera47f35272023-03-29 16:04:58 +02001935 "%-67s %4.2f %s%s"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001936 % (
1937 self.getDescription(test),
1938 time.time() - self.start_test,
1939 self.result_string,
Klement Sekera47f35272023-03-29 16:04:58 +02001940 result_code_to_suffix[self.result_code],
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001941 )
1942 )
juraj.linkescae64f82018-09-19 15:01:47 +02001943
Klement Sekera47f35272023-03-29 16:04:58 +02001944 self.send_result_through_pipe(test, TestResultCode.TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001945
Damjan Marionf56b77a2016-10-03 19:44:57 +02001946 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001947 """
1948 Print errors from running the test case
1949 """
juraj.linkesabec0122018-11-16 17:28:56 +01001950 if len(self.errors) > 0 or len(self.failures) > 0:
1951 self.stream.writeln()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001952 self.printErrorList("ERROR", self.errors)
1953 self.printErrorList("FAIL", self.failures)
juraj.linkesabec0122018-11-16 17:28:56 +01001954
1955 # ^^ that is the last output from unittest before summary
1956 if not self.runner.print_summary:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001957 devnull = unittest.runner._WritelnDecorator(open(os.devnull, "w"))
juraj.linkesabec0122018-11-16 17:28:56 +01001958 self.stream = devnull
1959 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001960
Damjan Marionf56b77a2016-10-03 19:44:57 +02001961 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001962 """
1963 Print error list to the output stream together with error type
1964 and test case description.
1965
1966 :param flavour: error type
1967 :param errors: iterable errors
1968
1969 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001970 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001971 self.stream.writeln(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001972 self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001973 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001974 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001975
1976
Damjan Marionf56b77a2016-10-03 19:44:57 +02001977class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001978 """
Klement Sekera104543f2017-02-03 07:29:43 +01001979 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001980 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001981
Klement Sekeraf62ae122016-10-11 11:47:09 +02001982 @property
1983 def resultclass(self):
1984 """Class maintaining the results of the tests"""
1985 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001986
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001987 def __init__(
1988 self,
1989 keep_alive_pipe=None,
1990 descriptions=True,
1991 verbosity=1,
1992 result_pipe=None,
1993 failfast=False,
1994 buffer=False,
1995 resultclass=None,
1996 print_summary=True,
1997 **kwargs,
1998 ):
Klement Sekera7a161da2017-01-17 13:42:48 +01001999 # ignore stream setting here, use hard-coded stdout to be in sync
2000 # with prints from VppTestCase methods ...
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002001 super(VppTestRunner, self).__init__(
2002 sys.stdout, descriptions, verbosity, failfast, buffer, resultclass, **kwargs
2003 )
juraj.linkesccfead62018-11-21 13:20:43 +01002004 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01002005
juraj.linkesabec0122018-11-16 17:28:56 +01002006 self.orig_stream = self.stream
2007 self.resultclass.test_framework_result_pipe = result_pipe
2008
2009 self.print_summary = print_summary
2010
2011 def _makeResult(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002012 return self.resultclass(self.stream, self.descriptions, self.verbosity, self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02002013
Damjan Marionf56b77a2016-10-03 19:44:57 +02002014 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02002015 """
2016 Run the tests
2017
2018 :param test:
2019
2020 """
Klement Sekera3658adc2017-06-07 08:19:47 +02002021 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02002022
2023 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01002024 if not self.print_summary:
2025 self.stream = self.orig_stream
2026 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02002027 return result
Neale Ranns812ed392017-10-16 04:20:13 -07002028
2029
2030class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002031 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
2032 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07002033 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002034 self.args = executable_args
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002035 if hasattr(self, "testcase") and self.testcase.debug_all:
Dave Wallace24564332019-10-21 02:53:14 +00002036 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002037 self.args = [
2038 "/usr/bin/gdbserver",
2039 "localhost:{port}".format(port=self.testcase.gdbserver_port),
2040 ] + args
2041 elif self.testcase.debug_gdb and hasattr(self, "wait_for_gdb"):
Dave Wallace24564332019-10-21 02:53:14 +00002042 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002043 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00002044 self.app_name = os.path.basename(self.app_bin)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002045 if hasattr(self, "role"):
2046 self.app_name += " {role}".format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002047 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07002048 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002049 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05002050 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07002051
Dave Wallace24564332019-10-21 02:53:14 +00002052 def wait_for_enter(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002053 if not hasattr(self, "testcase"):
Dave Wallace24564332019-10-21 02:53:14 +00002054 return
2055 if self.testcase.debug_all and self.testcase.debug_gdbserver:
2056 print()
2057 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002058 print(
2059 "Spawned GDB Server for '{app}' with PID: {pid}".format(
2060 app=self.app_name, pid=self.process.pid
2061 )
2062 )
Dave Wallace24564332019-10-21 02:53:14 +00002063 elif self.testcase.debug_all and self.testcase.debug_gdb:
2064 print()
2065 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002066 print(
2067 "Spawned '{app}' with PID: {pid}".format(
2068 app=self.app_name, pid=self.process.pid
2069 )
2070 )
Dave Wallace24564332019-10-21 02:53:14 +00002071 else:
2072 return
2073 print(single_line_delim)
2074 print("You can debug '{app}' using:".format(app=self.app_name))
2075 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002076 print(
2077 "sudo gdb "
2078 + self.app_bin
2079 + " -ex 'target remote localhost:{port}'".format(
2080 port=self.testcase.gdbserver_port
2081 )
2082 )
2083 print(
2084 "Now is the time to attach gdb by running the above "
2085 "command, set up breakpoints etc., then resume from "
2086 "within gdb by issuing the 'continue' command"
2087 )
Dave Wallace24564332019-10-21 02:53:14 +00002088 self.testcase.gdbserver_port += 1
2089 elif self.testcase.debug_gdb:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002090 print(
2091 "sudo gdb "
2092 + self.app_bin
2093 + " -ex 'attach {pid}'".format(pid=self.process.pid)
2094 )
2095 print(
2096 "Now is the time to attach gdb by running the above "
2097 "command and set up breakpoints etc., then resume from"
2098 " within gdb by issuing the 'continue' command"
2099 )
Dave Wallace24564332019-10-21 02:53:14 +00002100 print(single_line_delim)
2101 input("Press ENTER to continue running the testcase...")
2102
Neale Ranns812ed392017-10-16 04:20:13 -07002103 def run(self):
2104 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002105 if not os.path.exists(executable) or not os.access(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002106 executable, os.F_OK | os.X_OK
2107 ):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002108 # Exit code that means some system file did not exist,
2109 # could not be opened, or had some other kind of error.
2110 self.result = os.EX_OSFILE
2111 raise EnvironmentError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002112 "executable '%s' is not found or executable." % executable
2113 )
2114 self.logger.debug(
2115 "Running executable '{app}': '{cmd}'".format(
2116 app=self.app_name, cmd=" ".join(self.args)
2117 )
2118 )
Neale Ranns812ed392017-10-16 04:20:13 -07002119 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05002120 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07002121 env["CK_LOG_FILE_NAME"] = "-"
2122 self.process = subprocess.Popen(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002123 ["stdbuf", "-o0", "-e0"] + self.args,
2124 shell=False,
2125 env=env,
2126 preexec_fn=os.setpgrp,
2127 stdout=subprocess.PIPE,
2128 stderr=subprocess.PIPE,
2129 )
Dave Wallace24564332019-10-21 02:53:14 +00002130 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07002131 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00002132 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07002133 self.logger.info("Return code is `%s'" % self.process.returncode)
2134 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002135 self.logger.info(
2136 "Executable `{app}' wrote to stdout:".format(app=self.app_name)
2137 )
Neale Ranns812ed392017-10-16 04:20:13 -07002138 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002139 self.logger.info(out.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002140 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002141 self.logger.info(
2142 "Executable `{app}' wrote to stderr:".format(app=self.app_name)
2143 )
Neale Ranns812ed392017-10-16 04:20:13 -07002144 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002145 self.logger.info(err.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002146 self.logger.info(single_line_delim)
2147 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002148
Klement Sekera6aa58b72019-05-16 14:34:55 +02002149
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002150if __name__ == "__main__":
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002151 pass