blob: 230b2d57c5521ff6b879d179610a117a085c4ee4 [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
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080054
Klement Sekera558ceab2021-04-08 19:37:41 +020055
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050056logger = logging.getLogger(__name__)
57
58# Set up an empty logger for the testcase that can be overridden as necessary
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020059null_logger = logging.getLogger("VppTestCase")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050060null_logger.addHandler(logging.NullHandler())
61
juraj.linkescae64f82018-09-19 15:01:47 +020062PASS = 0
63FAIL = 1
64ERROR = 2
65SKIP = 3
66TEST_RUN = 4
Klement Sekera558ceab2021-04-08 19:37:41 +020067SKIP_CPU_SHORTAGE = 5
juraj.linkescae64f82018-09-19 15:01:47 +020068
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040069
Klement Sekerab23ffd72021-05-31 16:08:53 +020070if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010071 import debug_internal
72
Klement Sekeraf62ae122016-10-11 11:47:09 +020073"""
74 Test framework module.
75
76 The module provides a set of tools for constructing and running tests and
77 representing the results.
78"""
79
Klement Sekeraf62ae122016-10-11 11:47:09 +020080
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040081class VppDiedError(Exception):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020082 """exception for reporting that the subprocess has died."""
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040083
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020084 signals_by_value = {
85 v: k
86 for k, v in signal.__dict__.items()
87 if k.startswith("SIG") and not k.startswith("SIG_")
88 }
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040089
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040090 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040091 self.rv = rv
92 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040093 self.testcase = testcase
94 self.method_name = method_name
95
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040096 try:
97 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -040098 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040099 pass
100
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400101 if testcase is None and method_name is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200102 in_msg = ""
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400103 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200104 in_msg = " while running %s.%s" % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400105
Klement Sekera79a31db2021-03-12 18:16:10 +0100106 if self.rv:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200107 msg = "VPP subprocess died unexpectedly%s with return code: %d%s." % (
108 in_msg,
109 self.rv,
110 " [%s]" % (self.signal_name if self.signal_name is not None else ""),
111 )
Klement Sekera79a31db2021-03-12 18:16:10 +0100112 else:
113 msg = "VPP subprocess died unexpectedly%s." % in_msg
114
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400115 super(VppDiedError, self).__init__(msg)
116
117
Damjan Marionf56b77a2016-10-03 19:44:57 +0200118class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200119 """Private class to create packet info object.
120
121 Help process information about the next packet.
122 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200123 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200124
Matej Klotton86d87c42016-11-11 11:38:55 +0100125 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200126 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100127 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200128 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100129 #: Store the index of the destination packet generator interface
130 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200131 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100132 #: Store expected ip version
133 ip = -1
134 #: Store expected upper protocol
135 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100136 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200137 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200138
Matej Klotton16a14cd2016-12-07 15:09:13 +0100139 def __eq__(self, other):
140 index = self.index == other.index
141 src = self.src == other.src
142 dst = self.dst == other.dst
143 data = self.data == other.data
144 return index and src and dst and data
145
Klement Sekeraf62ae122016-10-11 11:47:09 +0200146
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100147def pump_output(testclass):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200148 """pump output from vpp stdout/stderr to proper queues"""
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100149 stdout_fragment = ""
150 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400151 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200152 readable = select.select(
153 [
154 testclass.vpp.stdout.fileno(),
155 testclass.vpp.stderr.fileno(),
156 testclass.pump_thread_wakeup_pipe[0],
157 ],
158 [],
159 [],
160 )[0]
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100161 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100162 read = os.read(testclass.vpp.stdout.fileno(), 102400)
163 if len(read) > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200164 split = read.decode("ascii", errors="backslashreplace").splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100165 if len(stdout_fragment) > 0:
166 split[0] = "%s%s" % (stdout_fragment, split[0])
167 if len(split) > 0 and split[-1].endswith("\n"):
168 limit = None
169 else:
170 limit = -1
171 stdout_fragment = split[-1]
172 testclass.vpp_stdout_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200173 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100174 for line in split[:limit]:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200175 testclass.logger.info("VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100176 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100177 read = os.read(testclass.vpp.stderr.fileno(), 102400)
178 if len(read) > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200179 split = read.decode("ascii", errors="backslashreplace").splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100180 if len(stderr_fragment) > 0:
181 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200182 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100183 limit = None
184 else:
185 limit = -1
186 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200187
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100188 testclass.vpp_stderr_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200189 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100190 for line in split[:limit]:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200191 testclass.logger.error("VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800192 # ignoring the dummy pipe here intentionally - the
193 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200194
195
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800196def _is_platform_aarch64():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200197 return platform.machine() == "aarch64"
juraj.linkes68ebc832018-11-29 09:37:08 +0100198
Klement Sekera6aa58b72019-05-16 14:34:55 +0200199
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800200is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100201
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800202
Dave Wallacee95b2462022-09-18 22:28:44 -0400203def _is_distro_ubuntu2204():
204 with open("/etc/os-release") as f:
205 for line in f.readlines():
206 if "jammy" in line:
207 return True
208 return False
209
210
211is_distro_ubuntu2204 = _is_distro_ubuntu2204()
212
213
Klement Sekera909a6a12017-08-08 04:33:53 +0200214class KeepAliveReporter(object):
215 """
216 Singleton object which reports test start to parent process
217 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200218
Klement Sekera909a6a12017-08-08 04:33:53 +0200219 _shared_state = {}
220
221 def __init__(self):
222 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800223 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200224
225 @property
226 def pipe(self):
227 return self._pipe
228
229 @pipe.setter
230 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800231 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200232 raise Exception("Internal error - pipe should only be set once.")
233 self._pipe = pipe
234
juraj.linkes40dd73b2018-09-21 13:55:16 +0200235 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200236 """
237 Write current test tmpdir & desc to keep-alive pipe to signal liveness
238 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200239 if self.pipe is None:
240 # if not running forked..
241 return
242
Klement Sekera909a6a12017-08-08 04:33:53 +0200243 if isclass(test):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200244 desc = "%s (%s)" % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200245 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200246 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200247
Klement Sekerab23ffd72021-05-31 16:08:53 +0200248 self.pipe.send((desc, config.vpp, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200249
250
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000251class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000252 # marks the suites that must run at the end
253 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000254 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000255 # marks the suites broken on VPP multi-worker
256 FIXME_VPP_WORKERS = 2
Naveen Joy6eaeea92021-09-09 17:57:02 -0700257 # marks the suites broken when ASan is enabled
258 FIXME_ASAN = 3
Dave Wallacee95b2462022-09-18 22:28:44 -0400259 # marks suites broken on Ubuntu-22.04
260 FIXME_UBUNTU2204 = 4
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000261
262
263def create_tag_decorator(e):
264 def decorator(cls):
265 try:
266 cls.test_tags.append(e)
267 except AttributeError:
268 cls.test_tags = [e]
269 return cls
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200270
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000271 return decorator
272
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000273
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000274tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000275tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Naveen Joy6eaeea92021-09-09 17:57:02 -0700276tag_fixme_asan = create_tag_decorator(TestCaseTag.FIXME_ASAN)
Dave Wallacee95b2462022-09-18 22:28:44 -0400277tag_fixme_ubuntu2204 = create_tag_decorator(TestCaseTag.FIXME_UBUNTU2204)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000278
279
Klement Sekerae2636852021-03-16 12:52:12 +0100280class DummyVpp:
281 returncode = None
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200282 pid = 0xCAFEBAFE
Klement Sekerae2636852021-03-16 12:52:12 +0100283
284 def poll(self):
285 pass
286
287 def terminate(self):
288 pass
289
290
Klement Sekera558ceab2021-04-08 19:37:41 +0200291class CPUInterface(ABC):
292 cpus = []
293 skipped_due_to_cpu_lack = False
294
295 @classmethod
296 @abstractmethod
297 def get_cpus_required(cls):
298 pass
299
300 @classmethod
301 def assign_cpus(cls, cpus):
302 cls.cpus = cpus
303
304
305class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100306 """This subclass is a base class for VPP test cases that are implemented as
307 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200308 """
309
Arthur de Kerhordb023802021-03-11 10:26:54 -0800310 extra_vpp_statseg_config = ""
Ole Troana45dc072018-12-21 16:04:22 +0100311 extra_vpp_punt_config = []
312 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500313 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400314 vapi_response_timeout = 5
Klement Sekera140af152022-02-18 10:30:51 +0000315 remove_configured_vpp_objects_on_tear_down = True
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100316
Klement Sekeraf62ae122016-10-11 11:47:09 +0200317 @property
318 def packet_infos(self):
319 """List of packet infos"""
320 return self._packet_infos
321
Klement Sekeradab231a2016-12-21 08:50:14 +0100322 @classmethod
323 def get_packet_count_for_if_idx(cls, dst_if_index):
324 """Get the number of packet info for specified destination if index"""
325 if dst_if_index in cls._packet_count_for_dst_if_idx:
326 return cls._packet_count_for_dst_if_idx[dst_if_index]
327 else:
328 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200329
330 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000331 def has_tag(cls, tag):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200332 """if the test case has a given tag - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000333 try:
334 return tag in cls.test_tags
335 except AttributeError:
336 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000337 return False
338
339 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000340 def is_tagged_run_solo(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200341 """if the test case class is timing-sensitive - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000342 return cls.has_tag(TestCaseTag.RUN_SOLO)
343
344 @classmethod
Naveen Joy6eaeea92021-09-09 17:57:02 -0700345 def skip_fixme_asan(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200346 """if @tag_fixme_asan & ASan is enabled - mark for skip"""
Naveen Joy6eaeea92021-09-09 17:57:02 -0700347 if cls.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200348 vpp_extra_cmake_args = os.environ.get("VPP_EXTRA_CMAKE_ARGS", "")
349 if "DVPP_ENABLE_SANITIZE_ADDR=ON" in vpp_extra_cmake_args:
Naveen Joy6eaeea92021-09-09 17:57:02 -0700350 cls = unittest.skip("Skipping @tag_fixme_asan tests")(cls)
351
352 @classmethod
Dave Wallacee95b2462022-09-18 22:28:44 -0400353 def skip_fixme_ubuntu2204(cls):
354 """if distro is ubuntu 22.04 and @tag_fixme_ubuntu2204 mark for skip"""
355 if cls.has_tag(TestCaseTag.FIXME_UBUNTU2204):
356 cls = unittest.skip("Skipping @tag_fixme_ubuntu2204 tests")(cls)
357
358 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200359 def instance(cls):
360 """Return the instance of this testcase"""
361 return cls.test_instance
362
Damjan Marionf56b77a2016-10-03 19:44:57 +0200363 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200364 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000365 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200366 cls.debug_core = False
367 cls.debug_gdb = False
368 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000369 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100370 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200371 if d is None:
372 return
373 dl = d.lower()
374 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200375 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000376 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200377 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000378 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200379 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100380 elif dl == "attach":
381 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200382 else:
383 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000384 if dl == "gdb-all" or dl == "gdbserver-all":
385 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200386
Klement Sekera558ceab2021-04-08 19:37:41 +0200387 @classmethod
388 def get_vpp_worker_count(cls):
389 if not hasattr(cls, "vpp_worker_count"):
390 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
391 cls.vpp_worker_count = 0
392 else:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200393 cls.vpp_worker_count = config.vpp_worker_count
Klement Sekera558ceab2021-04-08 19:37:41 +0200394 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200395
Klement Sekera558ceab2021-04-08 19:37:41 +0200396 @classmethod
397 def get_cpus_required(cls):
398 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200399
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800400 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200401 def setUpConstants(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200402 """Set-up the test case class based on environment variables"""
Klement Sekerab23ffd72021-05-31 16:08:53 +0200403 cls.step = config.step
404 cls.plugin_path = ":".join(config.vpp_plugin_dir)
405 cls.test_plugin_path = ":".join(config.vpp_test_plugin_dir)
406 cls.extern_plugin_path = ":".join(config.extern_plugin_dir)
Ole Troana45dc072018-12-21 16:04:22 +0100407 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100408 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100409 debug_cli = "cli-listen localhost:5002"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200410 size = re.search(r"\d+[gG]", config.coredump_size)
411 if size:
412 coredump_size = f"coredump-size {config.coredump_size}".lower()
413 else:
Ole Troana45dc072018-12-21 16:04:22 +0100414 coredump_size = "coredump-size unlimited"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200415 default_variant = config.variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000416 if default_variant is not None:
Benoît Ganne09ef5922022-07-29 10:52:34 +0200417 default_variant = "default { variant %s 100 }" % default_variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000418 else:
419 default_variant = ""
420
Klement Sekerab23ffd72021-05-31 16:08:53 +0200421 api_fuzzing = config.api_fuzz
Dave Barach77841402020-04-29 17:04:10 -0400422 if api_fuzzing is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200423 api_fuzzing = "off"
Dave Barach77841402020-04-29 17:04:10 -0400424
Klement Sekera8d815022021-03-15 16:58:10 +0100425 cls.vpp_cmdline = [
Klement Sekerab23ffd72021-05-31 16:08:53 +0200426 config.vpp,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200427 "unix",
428 "{",
429 "nodaemon",
430 debug_cli,
431 "full-coredump",
432 coredump_size,
433 "runtime-dir",
434 cls.tempdir,
Klement Sekera8d815022021-03-15 16:58:10 +0100435 "}",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200436 "api-trace",
437 "{",
438 "on",
439 "}",
440 "api-segment",
441 "{",
442 "prefix",
443 cls.get_api_segment_prefix(),
444 "}",
445 "cpu",
446 "{",
447 "main-core",
448 str(cls.cpus[0]),
449 ]
450 if cls.extern_plugin_path not in (None, ""):
451 cls.extra_vpp_plugin_config.append("add-path %s" % cls.extern_plugin_path)
452 if cls.get_vpp_worker_count():
453 cls.vpp_cmdline.extend(
454 ["corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])]
455 )
456 cls.vpp_cmdline.extend(
457 [
458 "}",
459 "physmem",
460 "{",
461 "max-size",
462 "32m",
463 "}",
464 "statseg",
465 "{",
466 "socket-name",
467 cls.get_stats_sock_path(),
468 cls.extra_vpp_statseg_config,
469 "}",
470 "socksvr",
471 "{",
472 "socket-name",
473 cls.get_api_sock_path(),
474 "}",
475 "node { ",
476 default_variant,
477 "}",
478 "api-fuzz {",
479 api_fuzzing,
480 "}",
481 "plugins",
482 "{",
483 "plugin",
484 "dpdk_plugin.so",
485 "{",
486 "disable",
487 "}",
488 "plugin",
489 "rdma_plugin.so",
490 "{",
491 "disable",
492 "}",
493 "plugin",
494 "lisp_unittest_plugin.so",
495 "{",
496 "enable",
497 "}",
498 "plugin",
499 "unittest_plugin.so",
500 "{",
501 "enable",
502 "}",
503 ]
504 + cls.extra_vpp_plugin_config
505 + [
506 "}",
507 ]
508 )
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000509
Ole Troana45dc072018-12-21 16:04:22 +0100510 if cls.extra_vpp_punt_config is not None:
511 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Dave Barach7d31ab22019-05-08 19:18:18 -0400512
Klement Sekerae2636852021-03-16 12:52:12 +0100513 if not cls.debug_attach:
514 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
515 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200516
517 @classmethod
518 def wait_for_enter(cls):
519 if cls.debug_gdbserver:
520 print(double_line_delim)
521 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
522 elif cls.debug_gdb:
523 print(double_line_delim)
524 print("Spawned VPP with PID: %d" % cls.vpp.pid)
525 else:
526 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
527 return
528 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000529 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200530 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200531 print(
532 f"sudo gdb {config.vpp} "
533 f"-ex 'target remote localhost:{cls.gdbserver_port}'"
534 )
535 print(
536 "Now is the time to attach gdb by running the above "
537 "command, set up breakpoints etc., then resume VPP from "
538 "within gdb by issuing the 'continue' command"
539 )
Dave Wallace24564332019-10-21 02:53:14 +0000540 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200541 elif cls.debug_gdb:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200542 print(f"sudo gdb {config.vpp} -ex 'attach {cls.vpp.pid}'")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200543 print(
544 "Now is the time to attach gdb by running the above "
545 "command and set up breakpoints etc., then resume VPP from"
546 " within gdb by issuing the 'continue' command"
547 )
Klement Sekera277b89c2016-10-28 13:20:27 +0200548 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800549 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200550
551 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100552 def attach_vpp(cls):
553 cls.vpp = DummyVpp()
554
555 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200556 def run_vpp(cls):
Klement Sekera558ceab2021-04-08 19:37:41 +0200557 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200558 cmdline = cls.vpp_cmdline
559
560 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200561 gdbserver = "/usr/bin/gdbserver"
562 if not os.path.isfile(gdbserver) or not os.access(gdbserver, os.X_OK):
563 raise Exception(
564 "gdbserver binary '%s' does not exist or is "
565 "not executable" % gdbserver
566 )
Klement Sekera931be3a2016-11-03 05:36:01 +0100567
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200568 cmdline = [
569 gdbserver,
570 "localhost:{port}".format(port=cls.gdbserver_port),
571 ] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200572 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
573
Klement Sekera931be3a2016-11-03 05:36:01 +0100574 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200575 cls.vpp = subprocess.Popen(
576 cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE
577 )
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800578 except subprocess.CalledProcessError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200579 cls.logger.critical(
580 "Subprocess returned with non-0 return code: (%s)", e.returncode
581 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800582 raise
583 except OSError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200584 cls.logger.critical(
585 "Subprocess returned with OS error: (%s) %s", e.errno, e.strerror
586 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800587 raise
588 except Exception as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200589 cls.logger.exception("Subprocess returned unexpected from %s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100590 raise
591
Klement Sekera277b89c2016-10-28 13:20:27 +0200592 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100593
Damjan Marionf56b77a2016-10-03 19:44:57 +0200594 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000595 def wait_for_coredump(cls):
596 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400597 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000598 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400599 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000600 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400601 ok = False
602 while time.time() < deadline:
603 cls.sleep(1)
604 size = curr_size
605 curr_size = os.path.getsize(corefile)
606 if size == curr_size:
607 ok = True
608 break
609 if not ok:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200610 cls.logger.error(
611 "Timed out waiting for coredump to complete: %s", corefile
612 )
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400613 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200614 cls.logger.error("Coredump complete: %s, size %d", corefile, curr_size)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400615
616 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100617 def get_stats_sock_path(cls):
618 return "%s/stats.sock" % cls.tempdir
619
620 @classmethod
621 def get_api_sock_path(cls):
622 return "%s/api.sock" % cls.tempdir
623
624 @classmethod
625 def get_api_segment_prefix(cls):
626 return os.path.basename(cls.tempdir) # Only used for VAPI
627
628 @classmethod
629 def get_tempdir(cls):
Klement Sekerab3fc6582022-03-10 11:47:45 +0100630 if cls.debug_attach:
631 tmpdir = f"{config.tmp_dir}/unittest-attach-gdb"
632 else:
633 tmpdir = f"{config.tmp_dir}/vpp-unittest-{cls.__name__}"
634 if config.wipe_tmp_dir:
635 shutil.rmtree(tmpdir, ignore_errors=True)
636 os.mkdir(tmpdir)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200637 return tmpdir
638
639 @classmethod
640 def create_file_handler(cls):
641 if config.log_dir is None:
642 cls.file_handler = FileHandler(f"{cls.tempdir}/log.txt")
643 return
644
645 logdir = f"{config.log_dir}/vpp-unittest-{cls.__name__}"
646 if config.wipe_tmp_dir:
647 shutil.rmtree(logdir, ignore_errors=True)
648 os.mkdir(logdir)
649 cls.file_handler = FileHandler(f"{logdir}/log.txt")
Klement Sekerae2636852021-03-16 12:52:12 +0100650
651 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200652 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200653 """
654 Perform class setup before running the testcase
655 Remove shared memory files, start vpp and connect the vpp-api
656 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800657 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100658 cls.logger = get_logger(cls.__name__)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200659 random.seed(config.rnd_seed)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200660 if hasattr(cls, "parallel_handler"):
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100661 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100662 cls.logger.propagate = False
Klement Sekerab23ffd72021-05-31 16:08:53 +0200663 cls.set_debug_flags(config.debug)
Klement Sekerae2636852021-03-16 12:52:12 +0100664 cls.tempdir = cls.get_tempdir()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200665 cls.create_file_handler()
Klement Sekera027dbd52017-04-11 06:01:53 +0200666 cls.file_handler.setFormatter(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200667 Formatter(fmt="%(asctime)s,%(msecs)03d %(message)s", datefmt="%H:%M:%S")
668 )
Klement Sekera027dbd52017-04-11 06:01:53 +0200669 cls.file_handler.setLevel(DEBUG)
670 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100671 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200672 os.chdir(cls.tempdir)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200673 cls.logger.info(
674 "Temporary dir is %s, api socket is %s",
675 cls.tempdir,
676 cls.get_api_sock_path(),
677 )
Klement Sekerab23ffd72021-05-31 16:08:53 +0200678 cls.logger.debug("Random seed is %s", config.rnd_seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200679 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100680 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200681 cls._pcaps = []
682 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200683 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100684 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100685 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200686 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200687 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200688 # need to catch exceptions here because if we raise, then the cleanup
689 # doesn't get called and we might end with a zombie vpp
690 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100691 if cls.debug_attach:
692 cls.attach_vpp()
693 else:
694 cls.run_vpp()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200695 cls.reporter.send_keep_alive(cls, "setUpClass")
juraj.linkes40dd73b2018-09-21 13:55:16 +0200696 VppTestResult.current_test_case_info = TestCaseInfo(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200697 cls.logger, cls.tempdir, cls.vpp.pid, config.vpp
698 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100699 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100700 cls.vpp_stderr_deque = deque()
Klement Sekerae2636852021-03-16 12:52:12 +0100701 if not cls.debug_attach:
702 cls.pump_thread_stop_flag = Event()
703 cls.pump_thread_wakeup_pipe = os.pipe()
704 cls.pump_thread = Thread(target=pump_output, args=(cls,))
705 cls.pump_thread.daemon = True
706 cls.pump_thread.start()
707 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400708 cls.vapi_response_timeout = 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200709 cls.vapi = VppPapiProvider(cls.__name__, cls, cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100710 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400711 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100712 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400713 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100714 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100715 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200716 try:
717 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100718 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200719 cls.vpp_startup_failed = True
720 cls.logger.critical(
721 "VPP died shortly after startup, check the"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200722 " output to standard error for possible cause"
723 )
Klement Sekera3747c752017-04-10 06:30:17 +0200724 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100725 try:
726 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100727 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500728 cls.logger.debug("Exception connecting to vapi: %s" % e)
729 cls.vapi.disconnect()
730
Klement Sekera085f5c02016-11-24 01:59:16 +0100731 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200732 print(
733 colorize(
734 "You're running VPP inside gdbserver but "
735 "VPP-API connection failed, did you forget "
736 "to 'continue' VPP from within gdb?",
737 RED,
738 )
739 )
Ole Troan4376ab22021-03-03 10:40:05 +0100740 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100741 if cls.debug_attach:
742 last_line = cls.vapi.cli("show thread").split("\n")[-2]
743 cls.vpp_worker_count = int(last_line.split(" ")[0])
744 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400745 except vpp_papi.VPPRuntimeError as e:
746 cls.logger.debug("%s" % e)
747 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100748 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000749 except Exception as e:
750 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400751 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100752 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200753
Damjan Marionf56b77a2016-10-03 19:44:57 +0200754 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500755 def _debug_quit(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200756 if cls.debug_gdbserver or cls.debug_gdb:
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500757 try:
758 cls.vpp.poll()
759
760 if cls.vpp.returncode is None:
761 print()
762 print(double_line_delim)
763 print("VPP or GDB server is still running")
764 print(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200765 input(
766 "When done debugging, press ENTER to kill the "
767 "process and finish running the testcase..."
768 )
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500769 except AttributeError:
770 pass
771
772 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200773 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200774 """
775 Disconnect vpp-api, kill vpp and cleanup shared memory files
776 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500777 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200778
juraj.linkes184870a2018-07-16 14:22:01 +0200779 # first signal that we want to stop the pump thread, then wake it up
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200780 if hasattr(cls, "pump_thread_stop_flag"):
juraj.linkes184870a2018-07-16 14:22:01 +0200781 cls.pump_thread_stop_flag.set()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200782 if hasattr(cls, "pump_thread_wakeup_pipe"):
783 os.write(cls.pump_thread_wakeup_pipe[1], b"ding dong wake up")
784 if hasattr(cls, "pump_thread"):
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100785 cls.logger.debug("Waiting for pump thread to stop")
786 cls.pump_thread.join()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200787 if hasattr(cls, "vpp_stderr_reader_thread"):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500788 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100789 cls.vpp_stderr_reader_thread.join()
790
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200791 if hasattr(cls, "vpp"):
792 if hasattr(cls, "vapi"):
Ole Troanfd574082019-11-27 23:12:48 +0100793 cls.logger.debug(cls.vapi.vpp.get_stats())
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200794 cls.logger.debug("Disconnecting class vapi client on %s", cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100795 cls.vapi.disconnect()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200796 cls.logger.debug("Deleting class vapi attribute on %s", cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100797 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200798 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100799 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000800 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100801 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400802 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100803 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100804 try:
805 outs, errs = cls.vpp.communicate(timeout=5)
806 except subprocess.TimeoutExpired:
807 cls.vpp.kill()
808 outs, errs = cls.vpp.communicate()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200809 cls.logger.debug("Deleting class vpp attribute on %s", cls.__name__)
Klement Sekerae2636852021-03-16 12:52:12 +0100810 if not cls.debug_attach:
811 cls.vpp.stdout.close()
812 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200813 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200814
Klement Sekera3747c752017-04-10 06:30:17 +0200815 if cls.vpp_startup_failed:
816 stdout_log = cls.logger.info
817 stderr_log = cls.logger.critical
818 else:
819 stdout_log = cls.logger.info
820 stderr_log = cls.logger.info
821
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200822 if hasattr(cls, "vpp_stdout_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200823 stdout_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200824 stdout_log("VPP output to stdout while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200825 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100826 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200827 with open(cls.tempdir + "/vpp_stdout.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200828 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200829 stdout_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200830 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200831
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200832 if hasattr(cls, "vpp_stderr_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200833 stderr_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200834 stderr_log("VPP output to stderr while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200835 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100836 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200837 with open(cls.tempdir + "/vpp_stderr.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200838 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200839 stderr_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200840 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200841
Damjan Marionf56b77a2016-10-03 19:44:57 +0200842 @classmethod
843 def tearDownClass(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200844 """Perform final cleanup after running all tests in this test-case"""
845 cls.logger.debug("--- tearDownClass() for %s called ---" % cls.__name__)
846 cls.reporter.send_keep_alive(cls, "tearDownClass")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200847 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200848 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100849 cls.reset_packet_infos()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200850 if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +0100851 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200852
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700853 def show_commands_at_teardown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200854 """Allow subclass specific teardown logging additions."""
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700855 self.logger.info("--- No test specific show commands provided. ---")
856
Damjan Marionf56b77a2016-10-03 19:44:57 +0200857 def tearDown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200858 """Show various debug prints after each test"""
859 self.logger.debug(
860 "--- tearDown() for %s.%s(%s) called ---"
861 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
862 )
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700863
864 try:
865 if not self.vpp_dead:
866 self.logger.debug(self.vapi.cli("show trace max 1000"))
867 self.logger.info(self.vapi.ppcli("show interface"))
868 self.logger.info(self.vapi.ppcli("show hardware"))
869 self.logger.info(self.statistics.set_errors_str())
870 self.logger.info(self.vapi.ppcli("show run"))
871 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400872 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700873 self.logger.info("Logging testcase specific show commands.")
874 self.show_commands_at_teardown()
Klement Sekera140af152022-02-18 10:30:51 +0000875 if self.remove_configured_vpp_objects_on_tear_down:
876 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500877 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000878 m = self._testMethodName
879 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500880 tmp_api_trace = "/tmp/%s" % api_trace
881 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
882 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200883 self.logger.info("Moving %s to %s\n" % (tmp_api_trace, vpp_api_trace_log))
Dave Wallace90c55722017-02-16 11:25:26 -0500884 os.rename(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100885 except VppTransportSocketIOError:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200886 self.logger.debug(
887 "VppTransportSocketIOError: Vpp dead. Cannot log show commands."
888 )
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700889 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100890 else:
891 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200892
Damjan Marionf56b77a2016-10-03 19:44:57 +0200893 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200894 """Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800895 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200896 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100897 if self.vpp_dead:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200898 raise VppDiedError(
899 rv=None,
900 testcase=self.__class__.__name__,
901 method_name=self._testMethodName,
902 )
903 self.sleep(0.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100904 self.vpp_stdout_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200905 "--- test setUp() for %s.%s(%s) starts here ---\n"
906 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
907 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100908 self.vpp_stderr_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200909 "--- test setUp() for %s.%s(%s) starts here ---\n"
910 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
911 )
Klement Sekeraf62ae122016-10-11 11:47:09 +0200912 self.vapi.cli("clear trace")
913 # store the test instance inside the test class - so that objects
914 # holding the class can access instance methods (like assertEqual)
915 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200916
Damjan Marionf56b77a2016-10-03 19:44:57 +0200917 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200918 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200919 """
920 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200921
Klement Sekera75e7d132017-09-20 08:26:30 +0200922 :param interfaces: iterable interface indexes (if None,
923 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200924
Klement Sekeraf62ae122016-10-11 11:47:09 +0200925 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200926 if interfaces is None:
927 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200928 for i in interfaces:
929 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200930
Damjan Marionf56b77a2016-10-03 19:44:57 +0200931 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200932 def register_pcap(cls, intf, worker):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200933 """Register a pcap in the testclass"""
Klement Sekera9225dee2016-12-12 08:36:58 +0100934 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200935 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100936
937 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000938 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400939 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
940 # returns float("2.190522")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200941 timestr = cls.vapi.cli("show clock")
942 head, sep, tail = timestr.partition(",")
943 head, sep, tail = head.partition("Time now")
Dave Barach19718002020-03-11 10:31:36 -0400944 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000945
946 @classmethod
947 def sleep_on_vpp_time(cls, sec):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200948 """Sleep according to time in VPP world"""
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000949 # On a busy system with many processes
950 # we might end up with VPP time being slower than real world
951 # So take that into account when waiting for VPP to do something
952 start_time = cls.get_vpp_time()
953 while cls.get_vpp_time() - start_time < sec:
954 cls.sleep(0.1)
955
956 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100957 def pg_start(cls, trace=True):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200958 """Enable the PG, wait till it is done, then clean up"""
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200959 for (intf, worker) in cls._old_pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200960 intf.handle_old_pcap_file(intf.get_in_path(worker), intf.in_history_counter)
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200961 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100962 if trace:
963 cls.vapi.cli("clear trace")
964 cls.vapi.cli("trace add pg-input 1000")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200965 cls.vapi.cli("packet-generator enable")
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000966 # PG, when starts, runs to completion -
967 # so let's avoid a race condition,
968 # and wait a little till it's done.
969 # Then clean it up - and then be gone.
970 deadline = time.time() + 300
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200971 while cls.vapi.cli("show packet-generator").find("Yes") != -1:
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000972 cls.sleep(0.01) # yield
973 if time.time() > deadline:
974 cls.logger.error("Timeout waiting for pg to stop")
975 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200976 for intf, worker in cls._pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200977 cls.vapi.cli("packet-generator delete %s" % intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200978 cls._old_pcaps = cls._pcaps
979 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200980
Damjan Marionf56b77a2016-10-03 19:44:57 +0200981 @classmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200982 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0, mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200983 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100984 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200985
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100986 :param interfaces: iterable indexes of the interfaces.
987 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200988
Klement Sekeraf62ae122016-10-11 11:47:09 +0200989 """
990 result = []
991 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +0000992 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200993 setattr(cls, intf.name, intf)
994 result.append(intf)
995 cls.pg_interfaces = result
996 return result
997
Matej Klotton0178d522016-11-04 11:11:44 +0100998 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000999 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
1000 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001001 return cls.create_pg_interfaces_internal(
1002 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP4
1003 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001004
1005 @classmethod
1006 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
1007 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001008 return cls.create_pg_interfaces_internal(
1009 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP6
1010 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001011
1012 @classmethod
1013 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
1014 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001015 return cls.create_pg_interfaces_internal(
1016 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1017 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001018
1019 @classmethod
1020 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
1021 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001022 return cls.create_pg_interfaces_internal(
1023 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1024 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001025
1026 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +02001027 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +01001028 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001029 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001030
Klement Sekerab9ef2732018-06-24 22:49:33 +02001031 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001032 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001033 """
Klement Sekerab9ef2732018-06-24 22:49:33 +02001034 result = [VppLoInterface(cls) for i in range(count)]
1035 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +01001036 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +01001037 cls.lo_interfaces = result
1038 return result
1039
Neale Ranns192b13f2019-03-15 02:16:20 -07001040 @classmethod
1041 def create_bvi_interfaces(cls, count):
1042 """
1043 Create BVI interfaces.
1044
1045 :param count: number of interfaces created.
1046 :returns: List of created interfaces.
1047 """
1048 result = [VppBviInterface(cls) for i in range(count)]
1049 for intf in result:
1050 setattr(cls, intf.name, intf)
1051 cls.bvi_interfaces = result
1052 return result
1053
Damjan Marionf56b77a2016-10-03 19:44:57 +02001054 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001055 def extend_packet(packet, size, padding=" "):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001056 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001057 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +02001058 NOTE: Currently works only when Raw layer is present.
1059
1060 :param packet: packet
1061 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +02001062 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +02001063
1064 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001065 packet_len = len(packet) + 4
1066 extend = size - packet_len
1067 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +02001068 num = (extend // len(padding)) + 1
1069 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +02001070
Klement Sekeradab231a2016-12-21 08:50:14 +01001071 @classmethod
1072 def reset_packet_infos(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001073 """Reset the list of packet info objects and packet counts to zero"""
Klement Sekeradab231a2016-12-21 08:50:14 +01001074 cls._packet_infos = {}
1075 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +02001076
Klement Sekeradab231a2016-12-21 08:50:14 +01001077 @classmethod
1078 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001079 """
1080 Create packet info object containing the source and destination indexes
1081 and add it to the testcase's packet info list
1082
Klement Sekeradab231a2016-12-21 08:50:14 +01001083 :param VppInterface src_if: source interface
1084 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +02001085
1086 :returns: _PacketInfo object
1087
1088 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001089 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +01001090 info.index = len(cls._packet_infos)
1091 info.src = src_if.sw_if_index
1092 info.dst = dst_if.sw_if_index
1093 if isinstance(dst_if, VppSubInterface):
1094 dst_idx = dst_if.parent.sw_if_index
1095 else:
1096 dst_idx = dst_if.sw_if_index
1097 if dst_idx in cls._packet_count_for_dst_if_idx:
1098 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1099 else:
1100 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1101 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001102 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001103
Damjan Marionf56b77a2016-10-03 19:44:57 +02001104 @staticmethod
1105 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001106 """
1107 Convert _PacketInfo object to packet payload
1108
1109 :param info: _PacketInfo object
1110
1111 :returns: string containing serialized data from packet info
1112 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001113
1114 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001115 return pack("iiiih", info.index, info.src, info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001116
Damjan Marionf56b77a2016-10-03 19:44:57 +02001117 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001118 def payload_to_info(payload, payload_field="load"):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001119 """
1120 Convert packet payload to _PacketInfo object
1121
1122 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001123 :type payload: <class 'scapy.packet.Raw'>
1124 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001125 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001126 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001127 :returns: _PacketInfo object containing de-serialized data from payload
1128
1129 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001130
1131 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1132 payload_b = getattr(payload, payload_field)[:18]
1133
Damjan Marionf56b77a2016-10-03 19:44:57 +02001134 info = _PacketInfo()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001135 info.index, info.src, info.dst, info.ip, info.proto = unpack("iiiih", payload_b)
Ray Kinsellab8165b92021-09-22 11:24:06 +01001136
1137 # some SRv6 TCs depend on get an exception if bad values are detected
1138 if info.index > 0x4000:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001139 raise ValueError("Index value is invalid")
Ray Kinsellab8165b92021-09-22 11:24:06 +01001140
Damjan Marionf56b77a2016-10-03 19:44:57 +02001141 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001142
Damjan Marionf56b77a2016-10-03 19:44:57 +02001143 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001144 """
1145 Iterate over the packet info list stored in the testcase
1146 Start iteration with first element if info is None
1147 Continue based on index in info if info is specified
1148
1149 :param info: info or None
1150 :returns: next info in list or None if no more infos
1151 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001152 if info is None:
1153 next_index = 0
1154 else:
1155 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001156 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001157 return None
1158 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001159 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001160
Klement Sekeraf62ae122016-10-11 11:47:09 +02001161 def get_next_packet_info_for_interface(self, src_index, info):
1162 """
1163 Search the packet info list for the next packet info with same source
1164 interface index
1165
1166 :param src_index: source interface index to search for
1167 :param info: packet info - where to start the search
1168 :returns: packet info or None
1169
1170 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001171 while True:
1172 info = self.get_next_packet_info(info)
1173 if info is None:
1174 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001175 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001176 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001177
Klement Sekeraf62ae122016-10-11 11:47:09 +02001178 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1179 """
1180 Search the packet info list for the next packet info with same source
1181 and destination interface indexes
1182
1183 :param src_index: source interface index to search for
1184 :param dst_index: destination interface index to search for
1185 :param info: packet info - where to start the search
1186 :returns: packet info or None
1187
1188 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001189 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001190 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001191 if info is None:
1192 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001193 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001194 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001195
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001196 def assert_equal(self, real_value, expected_value, name_or_class=None):
1197 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001198 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001199 return
1200 try:
1201 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001202 msg = msg % (
1203 getdoc(name_or_class).strip(),
1204 real_value,
1205 str(name_or_class(real_value)),
1206 expected_value,
1207 str(name_or_class(expected_value)),
1208 )
Klement Sekera13a83ef2018-03-21 12:35:51 +01001209 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001210 msg = "Invalid %s: %s does not match expected value %s" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001211 name_or_class,
1212 real_value,
1213 expected_value,
1214 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001215
1216 self.assertEqual(real_value, expected_value, msg)
1217
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001218 def assert_in_range(self, real_value, expected_min, expected_max, name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001219 if name is None:
1220 msg = None
1221 else:
1222 msg = "Invalid %s: %s out of range <%s,%s>" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001223 name,
1224 real_value,
1225 expected_min,
1226 expected_max,
1227 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001228 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1229
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001230 def assert_packet_checksums_valid(self, packet, ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001231 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001232 udp_layers = ["UDP", "UDPerror"]
1233 checksum_fields = ["cksum", "chksum"]
Klement Sekerad81ae412018-05-16 10:52:54 +02001234 checksums = []
1235 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001236 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001237 while True:
1238 layer = temp.getlayer(counter)
1239 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001240 layer = layer.copy()
1241 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001242 for cf in checksum_fields:
1243 if hasattr(layer, cf):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001244 if (
1245 ignore_zero_udp_checksums
1246 and 0 == getattr(layer, cf)
1247 and layer.name in udp_layers
1248 ):
Klement Sekerad81ae412018-05-16 10:52:54 +02001249 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001250 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001251 checksums.append((counter, cf))
1252 else:
1253 break
1254 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001255 if 0 == len(checksums):
1256 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001257 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001258 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001259 calc_sum = getattr(temp[layer], cf)
1260 self.assert_equal(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001261 getattr(received[layer], cf),
1262 calc_sum,
1263 "packet checksum on layer #%d: %s" % (layer, temp[layer].name),
1264 )
Klement Sekera31da2e32018-06-24 22:49:55 +02001265 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001266 "Checksum field `%s` on `%s` layer has correct value `%s`"
1267 % (cf, temp[layer].name, calc_sum)
1268 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001269
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001270 def assert_checksum_valid(
1271 self, received_packet, layer, field_name="chksum", ignore_zero_checksum=False
1272 ):
1273 """Check checksum of received packet on given layer"""
Klement Sekerad81ae412018-05-16 10:52:54 +02001274 received_packet_checksum = getattr(received_packet[layer], field_name)
1275 if ignore_zero_checksum and 0 == received_packet_checksum:
1276 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001277 recalculated = received_packet.__class__(scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001278 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001279 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001280 self.assert_equal(
1281 received_packet_checksum,
1282 getattr(recalculated[layer], field_name),
1283 "packet checksum on layer: %s" % layer,
1284 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001285
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001286 def assert_ip_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1287 self.assert_checksum_valid(
1288 received_packet, "IP", ignore_zero_checksum=ignore_zero_checksum
1289 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001290
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001291 def assert_tcp_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1292 self.assert_checksum_valid(
1293 received_packet, "TCP", ignore_zero_checksum=ignore_zero_checksum
1294 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001295
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001296 def assert_udp_checksum_valid(self, received_packet, ignore_zero_checksum=True):
1297 self.assert_checksum_valid(
1298 received_packet, "UDP", ignore_zero_checksum=ignore_zero_checksum
1299 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001300
1301 def assert_embedded_icmp_checksum_valid(self, received_packet):
1302 if received_packet.haslayer(IPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001303 self.assert_checksum_valid(received_packet, "IPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001304 if received_packet.haslayer(TCPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001305 self.assert_checksum_valid(received_packet, "TCPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001306 if received_packet.haslayer(UDPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001307 self.assert_checksum_valid(
1308 received_packet, "UDPerror", ignore_zero_checksum=True
1309 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001310 if received_packet.haslayer(ICMPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001311 self.assert_checksum_valid(received_packet, "ICMPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001312
1313 def assert_icmp_checksum_valid(self, received_packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001314 self.assert_checksum_valid(received_packet, "ICMP")
Klement Sekerad81ae412018-05-16 10:52:54 +02001315 self.assert_embedded_icmp_checksum_valid(received_packet)
1316
1317 def assert_icmpv6_checksum_valid(self, pkt):
1318 if pkt.haslayer(ICMPv6DestUnreach):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001319 self.assert_checksum_valid(pkt, "ICMPv6DestUnreach", "cksum")
Klement Sekerad81ae412018-05-16 10:52:54 +02001320 self.assert_embedded_icmp_checksum_valid(pkt)
1321 if pkt.haslayer(ICMPv6EchoRequest):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001322 self.assert_checksum_valid(pkt, "ICMPv6EchoRequest", "cksum")
Klement Sekerad81ae412018-05-16 10:52:54 +02001323 if pkt.haslayer(ICMPv6EchoReply):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001324 self.assert_checksum_valid(pkt, "ICMPv6EchoReply", "cksum")
Klement Sekerad81ae412018-05-16 10:52:54 +02001325
Klement Sekera107ad732022-02-18 10:32:08 +00001326 def get_counter(self, counter):
Klement Sekera3a343d42019-05-16 14:35:46 +02001327 if counter.startswith("/"):
1328 counter_value = self.statistics.get_counter(counter)
1329 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001330 counters = self.vapi.cli("sh errors").split("\n")
Klement Sekera6aa58b72019-05-16 14:34:55 +02001331 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001332 for i in range(1, len(counters) - 1):
1333 results = counters[i].split()
1334 if results[1] == counter:
1335 counter_value = int(results[0])
1336 break
1337 return counter_value
1338
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001339 def assert_counter_equal(self, counter, expected_value, thread=None, index=0):
Klement Sekera107ad732022-02-18 10:32:08 +00001340 c = self.get_counter(counter)
1341 if thread is not None:
1342 c = c[thread][index]
1343 else:
1344 c = sum(x[index] for x in c)
1345 self.assert_equal(c, expected_value, "counter `%s'" % counter)
1346
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001347 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera107ad732022-02-18 10:32:08 +00001348 counter_value = self.get_counter(counter)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001349 self.assert_equal(
1350 counter_value, expected_value, "packet counter `%s'" % counter
1351 )
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001352
Ole Troan233e4682019-05-16 15:01:34 +02001353 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001354 counter_value = self.statistics[counter].sum()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001355 self.assert_equal(counter_value, expected_value, "error counter `%s'" % counter)
Ole Troan233e4682019-05-16 15:01:34 +02001356
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001357 @classmethod
1358 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001359
1360 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1361 # * by Guido, only the main thread can be interrupted.
1362 # */
1363 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1364 if timeout == 0:
1365 # yield quantum
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001366 if hasattr(os, "sched_yield"):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001367 os.sched_yield()
1368 else:
1369 time.sleep(0)
1370 return
1371
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001372 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001373 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001374 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001375 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001376 if after - before > 2 * timeout:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001377 cls.logger.error(
1378 "unexpected self.sleep() result - slept for %es instead of ~%es!",
1379 after - before,
1380 timeout,
1381 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001382
1383 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001384 "Finished sleep (%s) - slept %es (wanted %es)",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001385 remark,
1386 after - before,
1387 timeout,
1388 )
Klement Sekeraa57a9702017-02-02 06:58:07 +01001389
Benoît Ganne56eccdb2021-08-20 09:18:31 +02001390 def virtual_sleep(self, timeout, remark=None):
1391 self.logger.debug("Moving VPP time by %s (%s)", timeout, remark)
1392 self.vapi.cli("set clock adjust %s" % timeout)
1393
Benoît Ganne8c45e512021-02-19 16:39:13 +01001394 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001395 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001396 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001397 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001398
Klement Sekeraad3187f2022-02-18 10:34:35 +00001399 def snapshot_stats(self, stats_diff):
1400 """Return snapshot of interesting stats based on diff dictionary."""
1401 stats_snapshot = {}
1402 for sw_if_index in stats_diff:
1403 for counter in stats_diff[sw_if_index]:
1404 stats_snapshot[counter] = self.statistics[counter]
1405 self.logger.debug(f"Took statistics stats_snapshot: {stats_snapshot}")
1406 return stats_snapshot
1407
1408 def compare_stats_with_snapshot(self, stats_diff, stats_snapshot):
1409 """Assert appropriate difference between current stats and snapshot."""
1410 for sw_if_index in stats_diff:
1411 for cntr, diff in stats_diff[sw_if_index].items():
1412 if sw_if_index == "err":
1413 self.assert_equal(
1414 self.statistics[cntr].sum(),
1415 stats_snapshot[cntr].sum() + diff,
1416 f"'{cntr}' counter value (previous value: "
1417 f"{stats_snapshot[cntr].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001418 f"expected diff: {diff})",
1419 )
Klement Sekeraad3187f2022-02-18 10:34:35 +00001420 else:
1421 try:
1422 self.assert_equal(
1423 self.statistics[cntr][:, sw_if_index].sum(),
1424 stats_snapshot[cntr][:, sw_if_index].sum() + diff,
1425 f"'{cntr}' counter value (previous value: "
1426 f"{stats_snapshot[cntr][:, sw_if_index].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001427 f"expected diff: {diff})",
1428 )
Klement Sekeraad3187f2022-02-18 10:34:35 +00001429 except IndexError:
1430 # if diff is 0, then this most probably a case where
1431 # test declares multiple interfaces but traffic hasn't
1432 # passed through this one yet - which means the counter
1433 # value is 0 and can be ignored
1434 if 0 != diff:
1435 raise
1436
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001437 def send_and_assert_no_replies(
1438 self, intf, pkts, remark="", timeout=None, stats_diff=None, trace=True, msg=None
1439 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001440 if stats_diff:
1441 stats_snapshot = self.snapshot_stats(stats_diff)
1442
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001443 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001444
1445 try:
1446 if not timeout:
1447 timeout = 1
1448 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +00001449 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001450 timeout = 0.1
1451 finally:
1452 if trace:
1453 if msg:
1454 self.logger.debug(f"send_and_assert_no_replies: {msg}")
1455 self.logger.debug(self.vapi.cli("show trace"))
1456
1457 if stats_diff:
1458 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -08001459
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001460 def send_and_expect(
1461 self,
1462 intf,
1463 pkts,
1464 output,
1465 n_rx=None,
1466 worker=None,
1467 trace=True,
1468 msg=None,
1469 stats_diff=None,
1470 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001471 if stats_diff:
1472 stats_snapshot = self.snapshot_stats(stats_diff)
1473
Neale Rannsd7603d92019-03-28 08:56:10 +00001474 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +00001475 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001476 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001477 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +02001478 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001479 if msg:
1480 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +02001481 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +00001482
1483 if stats_diff:
1484 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1485
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001486 return rx
1487
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001488 def send_and_expect_load_balancing(
1489 self, input, pkts, outputs, worker=None, trace=True
1490 ):
Neale Ranns699bea22022-02-17 09:22:16 +00001491 self.pg_send(input, pkts, worker=worker, trace=trace)
1492 rxs = []
1493 for oo in outputs:
1494 rx = oo._get_capture(1)
1495 self.assertNotEqual(0, len(rx))
1496 rxs.append(rx)
1497 if trace:
1498 self.logger.debug(self.vapi.cli("show trace"))
1499 return rxs
1500
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001501 def send_and_expect_some(self, intf, pkts, output, worker=None, trace=True):
Neale Ranns5c6dd172022-02-17 09:08:47 +00001502 self.pg_send(intf, pkts, worker=worker, trace=trace)
1503 rx = output._get_capture(1)
1504 if trace:
1505 self.logger.debug(self.vapi.cli("show trace"))
1506 self.assertTrue(len(rx) > 0)
1507 self.assertTrue(len(rx) < len(pkts))
1508 return rx
1509
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001510 def send_and_expect_only(self, intf, pkts, output, timeout=None, stats_diff=None):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001511 if stats_diff:
1512 stats_snapshot = self.snapshot_stats(stats_diff)
1513
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001514 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001515 rx = output.get_capture(len(pkts))
1516 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001517 if not timeout:
1518 timeout = 1
1519 for i in self.pg_interfaces:
1520 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +00001521 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001522 timeout = 0.1
1523
Klement Sekeraad3187f2022-02-18 10:34:35 +00001524 if stats_diff:
1525 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1526
Neale Ranns52fae862018-01-08 04:41:42 -08001527 return rx
1528
Damjan Marionf56b77a2016-10-03 19:44:57 +02001529
juraj.linkes184870a2018-07-16 14:22:01 +02001530def get_testcase_doc_name(test):
1531 return getdoc(test.__class__).splitlines()[0]
1532
1533
Ole Trøan5ba91592018-11-22 10:01:09 +00001534def get_test_description(descriptions, test):
1535 short_description = test.shortDescription()
1536 if descriptions and short_description:
1537 return short_description
1538 else:
1539 return str(test)
1540
1541
juraj.linkes40dd73b2018-09-21 13:55:16 +02001542class TestCaseInfo(object):
1543 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1544 self.logger = logger
1545 self.tempdir = tempdir
1546 self.vpp_pid = vpp_pid
1547 self.vpp_bin_path = vpp_bin_path
1548 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001549
1550
Damjan Marionf56b77a2016-10-03 19:44:57 +02001551class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001552 """
1553 @property result_string
1554 String variable to store the test case result string.
1555 @property errors
1556 List variable containing 2-tuples of TestCase instances and strings
1557 holding formatted tracebacks. Each tuple represents a test which
1558 raised an unexpected exception.
1559 @property failures
1560 List variable containing 2-tuples of TestCase instances and strings
1561 holding formatted tracebacks. Each tuple represents a test where
1562 a failure was explicitly signalled using the TestCase.assert*()
1563 methods.
1564 """
1565
juraj.linkes40dd73b2018-09-21 13:55:16 +02001566 failed_test_cases_info = set()
1567 core_crash_test_cases_info = set()
1568 current_test_case_info = None
1569
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001570 def __init__(self, stream=None, descriptions=None, verbosity=None, runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001571 """
Klement Sekerada505f62017-01-04 12:58:53 +01001572 :param stream File descriptor to store where to report test results.
1573 Set to the standard error stream by default.
1574 :param descriptions Boolean variable to store information if to use
1575 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001576 :param verbosity Integer variable to store required verbosity level.
1577 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001578 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001579 self.stream = stream
1580 self.descriptions = descriptions
1581 self.verbosity = verbosity
1582 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001583 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001584 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001585
Damjan Marionf56b77a2016-10-03 19:44:57 +02001586 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001587 """
1588 Record a test succeeded result
1589
1590 :param test:
1591
1592 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001593 if self.current_test_case_info:
1594 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001595 "--- addSuccess() %s.%s(%s) called"
1596 % (test.__class__.__name__, test._testMethodName, test._testMethodDoc)
1597 )
Damjan Marionf56b77a2016-10-03 19:44:57 +02001598 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001599 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001600
juraj.linkescae64f82018-09-19 15:01:47 +02001601 self.send_result_through_pipe(test, PASS)
1602
Klement Sekeraf62ae122016-10-11 11:47:09 +02001603 def addSkip(self, test, reason):
1604 """
1605 Record a test skipped.
1606
1607 :param test:
1608 :param reason:
1609
1610 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001611 if self.current_test_case_info:
1612 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001613 "--- addSkip() %s.%s(%s) called, reason is %s"
1614 % (
1615 test.__class__.__name__,
1616 test._testMethodName,
1617 test._testMethodDoc,
1618 reason,
1619 )
1620 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001621 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001622 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001623
Klement Sekera558ceab2021-04-08 19:37:41 +02001624 if reason == "not enough cpus":
1625 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1626 else:
1627 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001628
juraj.linkes40dd73b2018-09-21 13:55:16 +02001629 def symlink_failed(self):
1630 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001631 try:
Klement Sekerab23ffd72021-05-31 16:08:53 +02001632 failed_dir = config.failed_dir
juraj.linkes40dd73b2018-09-21 13:55:16 +02001633 link_path = os.path.join(
1634 failed_dir,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001635 "%s-FAILED" % os.path.basename(self.current_test_case_info.tempdir),
1636 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001637
1638 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001639 "creating a link to the failed test"
1640 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001641 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001642 "os.symlink(%s, %s)"
1643 % (self.current_test_case_info.tempdir, link_path)
1644 )
juraj.linkes184870a2018-07-16 14:22:01 +02001645 if os.path.exists(link_path):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001646 self.current_test_case_info.logger.debug("symlink already exists")
juraj.linkes184870a2018-07-16 14:22:01 +02001647 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001648 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001649
Klement Sekeraf413bef2017-08-15 07:09:02 +02001650 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001651 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001652
juraj.linkescae64f82018-09-19 15:01:47 +02001653 def send_result_through_pipe(self, test, result):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001654 if hasattr(self, "test_framework_result_pipe"):
juraj.linkescae64f82018-09-19 15:01:47 +02001655 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001656 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001657 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001658
juraj.linkes40dd73b2018-09-21 13:55:16 +02001659 def log_error(self, test, err, fn_name):
1660 if self.current_test_case_info:
1661 if isinstance(test, unittest.suite._ErrorHolder):
1662 test_name = test.description
1663 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001664 test_name = "%s.%s(%s)" % (
1665 test.__class__.__name__,
1666 test._testMethodName,
1667 test._testMethodDoc,
1668 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001669 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001670 "--- %s() %s called, err is %s" % (fn_name, test_name, err)
1671 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001672 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001673 "formatted exception is:\n%s" % "".join(format_exception(*err))
1674 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001675
1676 def add_error(self, test, err, unittest_fn, error_type):
1677 if error_type == FAIL:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001678 self.log_error(test, err, "addFailure")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001679 error_type_str = colorize("FAIL", RED)
1680 elif error_type == ERROR:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001681 self.log_error(test, err, "addError")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001682 error_type_str = colorize("ERROR", RED)
1683 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001684 raise Exception(
1685 "Error type %s cannot be used to record an "
1686 "error or a failure" % error_type
1687 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001688
1689 unittest_fn(self, test, err)
1690 if self.current_test_case_info:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001691 self.result_string = "%s [ temp dir used by test case: %s ]" % (
1692 error_type_str,
1693 self.current_test_case_info.tempdir,
1694 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001695 self.symlink_failed()
1696 self.failed_test_cases_info.add(self.current_test_case_info)
1697 if is_core_present(self.current_test_case_info.tempdir):
1698 if not self.current_test_case_info.core_crash_test:
1699 if isinstance(test, unittest.suite._ErrorHolder):
1700 test_name = str(test)
1701 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001702 test_name = "'{!s}' ({!s})".format(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001703 get_testcase_doc_name(test), test.id()
1704 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001705 self.current_test_case_info.core_crash_test = test_name
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001706 self.core_crash_test_cases_info.add(self.current_test_case_info)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001707 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001708 self.result_string = "%s [no temp dir]" % error_type_str
juraj.linkes40dd73b2018-09-21 13:55:16 +02001709
1710 self.send_result_through_pipe(test, error_type)
1711
Damjan Marionf56b77a2016-10-03 19:44:57 +02001712 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001713 """
1714 Record a test failed result
1715
1716 :param test:
1717 :param err: error message
1718
1719 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001720 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001721
Damjan Marionf56b77a2016-10-03 19:44:57 +02001722 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001723 """
1724 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001725
Klement Sekeraf62ae122016-10-11 11:47:09 +02001726 :param test:
1727 :param err: error message
1728
1729 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001730 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001731
Damjan Marionf56b77a2016-10-03 19:44:57 +02001732 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001733 """
1734 Get test description
1735
1736 :param test:
1737 :returns: test description
1738
1739 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001740 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001741
Damjan Marionf56b77a2016-10-03 19:44:57 +02001742 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001743 """
1744 Start a test
1745
1746 :param test:
1747
1748 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001749
1750 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001751 if test.__class__ in self.printed:
1752 return
1753
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001754 test_doc = getdoc(test)
1755 if not test_doc:
1756 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001757
Klement Sekeraea6236b2021-03-25 14:03:44 +01001758 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001759 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001760 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001761 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001762
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001763 # This block may overwrite the colorized title above,
1764 # but we want this to stand out and be fixed
1765 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001766 test_title = colorize(f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001767
Naveen Joy6eaeea92021-09-09 17:57:02 -07001768 if test.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001769 test_title = colorize(f"FIXME with ASAN: {test_title}", RED)
Naveen Joy6eaeea92021-09-09 17:57:02 -07001770 test.skip_fixme_asan()
1771
Dave Wallacee95b2462022-09-18 22:28:44 -04001772 if is_distro_ubuntu2204 == True and test.has_tag(
1773 TestCaseTag.FIXME_UBUNTU2204
1774 ):
1775 test_title = colorize(f"FIXME on Ubuntu-22.04: {test_title}", RED)
1776 test.skip_fixme_ubuntu2204()
1777
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001778 if hasattr(test, "vpp_worker_count"):
Klement Sekeraea6236b2021-03-25 14:03:44 +01001779 if test.vpp_worker_count == 0:
1780 test_title += " [main thread only]"
1781 elif test.vpp_worker_count == 1:
1782 test_title += " [1 worker thread]"
1783 else:
1784 test_title += f" [{test.vpp_worker_count} worker threads]"
1785
Klement Sekera558ceab2021-04-08 19:37:41 +02001786 if test.__class__.skipped_due_to_cpu_lack:
1787 test_title = colorize(
1788 f"{test_title} [skipped - not enough cpus, "
1789 f"required={test.__class__.get_cpus_required()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001790 f"available={max_vpp_cpus}]",
1791 YELLOW,
1792 )
Klement Sekera558ceab2021-04-08 19:37:41 +02001793
1794 print(double_line_delim)
1795 print(test_title)
1796 print(double_line_delim)
1797 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001798
1799 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001800 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001801 unittest.TestResult.startTest(self, test)
1802 if self.verbosity > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001803 self.stream.writeln("Starting " + self.getDescription(test) + " ...")
Klement Sekeraf62ae122016-10-11 11:47:09 +02001804 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001805
Damjan Marionf56b77a2016-10-03 19:44:57 +02001806 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001807 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001808 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001809
1810 :param test:
1811
1812 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001813 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001814
Damjan Marionf56b77a2016-10-03 19:44:57 +02001815 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001816 self.stream.writeln(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001817 self.stream.writeln(
1818 "%-73s%s" % (self.getDescription(test), self.result_string)
1819 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001820 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001821 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001822 self.stream.writeln(
1823 "%-68s %4.2f %s"
1824 % (
1825 self.getDescription(test),
1826 time.time() - self.start_test,
1827 self.result_string,
1828 )
1829 )
juraj.linkescae64f82018-09-19 15:01:47 +02001830
1831 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001832
Damjan Marionf56b77a2016-10-03 19:44:57 +02001833 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001834 """
1835 Print errors from running the test case
1836 """
juraj.linkesabec0122018-11-16 17:28:56 +01001837 if len(self.errors) > 0 or len(self.failures) > 0:
1838 self.stream.writeln()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001839 self.printErrorList("ERROR", self.errors)
1840 self.printErrorList("FAIL", self.failures)
juraj.linkesabec0122018-11-16 17:28:56 +01001841
1842 # ^^ that is the last output from unittest before summary
1843 if not self.runner.print_summary:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001844 devnull = unittest.runner._WritelnDecorator(open(os.devnull, "w"))
juraj.linkesabec0122018-11-16 17:28:56 +01001845 self.stream = devnull
1846 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001847
Damjan Marionf56b77a2016-10-03 19:44:57 +02001848 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001849 """
1850 Print error list to the output stream together with error type
1851 and test case description.
1852
1853 :param flavour: error type
1854 :param errors: iterable errors
1855
1856 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001857 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001858 self.stream.writeln(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001859 self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001860 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001861 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001862
1863
Damjan Marionf56b77a2016-10-03 19:44:57 +02001864class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001865 """
Klement Sekera104543f2017-02-03 07:29:43 +01001866 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001867 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001868
Klement Sekeraf62ae122016-10-11 11:47:09 +02001869 @property
1870 def resultclass(self):
1871 """Class maintaining the results of the tests"""
1872 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001873
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001874 def __init__(
1875 self,
1876 keep_alive_pipe=None,
1877 descriptions=True,
1878 verbosity=1,
1879 result_pipe=None,
1880 failfast=False,
1881 buffer=False,
1882 resultclass=None,
1883 print_summary=True,
1884 **kwargs,
1885 ):
Klement Sekera7a161da2017-01-17 13:42:48 +01001886 # ignore stream setting here, use hard-coded stdout to be in sync
1887 # with prints from VppTestCase methods ...
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001888 super(VppTestRunner, self).__init__(
1889 sys.stdout, descriptions, verbosity, failfast, buffer, resultclass, **kwargs
1890 )
juraj.linkesccfead62018-11-21 13:20:43 +01001891 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001892
juraj.linkesabec0122018-11-16 17:28:56 +01001893 self.orig_stream = self.stream
1894 self.resultclass.test_framework_result_pipe = result_pipe
1895
1896 self.print_summary = print_summary
1897
1898 def _makeResult(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001899 return self.resultclass(self.stream, self.descriptions, self.verbosity, self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001900
Damjan Marionf56b77a2016-10-03 19:44:57 +02001901 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001902 """
1903 Run the tests
1904
1905 :param test:
1906
1907 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001908 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001909
1910 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001911 if not self.print_summary:
1912 self.stream = self.orig_stream
1913 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001914 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001915
1916
1917class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001918 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1919 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001920 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001921 self.args = executable_args
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001922 if hasattr(self, "testcase") and self.testcase.debug_all:
Dave Wallace24564332019-10-21 02:53:14 +00001923 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001924 self.args = [
1925 "/usr/bin/gdbserver",
1926 "localhost:{port}".format(port=self.testcase.gdbserver_port),
1927 ] + args
1928 elif self.testcase.debug_gdb and hasattr(self, "wait_for_gdb"):
Dave Wallace24564332019-10-21 02:53:14 +00001929 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001930 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001931 self.app_name = os.path.basename(self.app_bin)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001932 if hasattr(self, "role"):
1933 self.app_name += " {role}".format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001934 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001935 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001936 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001937 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001938
Dave Wallace24564332019-10-21 02:53:14 +00001939 def wait_for_enter(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001940 if not hasattr(self, "testcase"):
Dave Wallace24564332019-10-21 02:53:14 +00001941 return
1942 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1943 print()
1944 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001945 print(
1946 "Spawned GDB Server for '{app}' with PID: {pid}".format(
1947 app=self.app_name, pid=self.process.pid
1948 )
1949 )
Dave Wallace24564332019-10-21 02:53:14 +00001950 elif self.testcase.debug_all and self.testcase.debug_gdb:
1951 print()
1952 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001953 print(
1954 "Spawned '{app}' with PID: {pid}".format(
1955 app=self.app_name, pid=self.process.pid
1956 )
1957 )
Dave Wallace24564332019-10-21 02:53:14 +00001958 else:
1959 return
1960 print(single_line_delim)
1961 print("You can debug '{app}' using:".format(app=self.app_name))
1962 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001963 print(
1964 "sudo gdb "
1965 + self.app_bin
1966 + " -ex 'target remote localhost:{port}'".format(
1967 port=self.testcase.gdbserver_port
1968 )
1969 )
1970 print(
1971 "Now is the time to attach gdb by running the above "
1972 "command, set up breakpoints etc., then resume from "
1973 "within gdb by issuing the 'continue' command"
1974 )
Dave Wallace24564332019-10-21 02:53:14 +00001975 self.testcase.gdbserver_port += 1
1976 elif self.testcase.debug_gdb:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001977 print(
1978 "sudo gdb "
1979 + self.app_bin
1980 + " -ex 'attach {pid}'".format(pid=self.process.pid)
1981 )
1982 print(
1983 "Now is the time to attach gdb by running the above "
1984 "command and set up breakpoints etc., then resume from"
1985 " within gdb by issuing the 'continue' command"
1986 )
Dave Wallace24564332019-10-21 02:53:14 +00001987 print(single_line_delim)
1988 input("Press ENTER to continue running the testcase...")
1989
Neale Ranns812ed392017-10-16 04:20:13 -07001990 def run(self):
1991 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001992 if not os.path.exists(executable) or not os.access(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001993 executable, os.F_OK | os.X_OK
1994 ):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001995 # Exit code that means some system file did not exist,
1996 # could not be opened, or had some other kind of error.
1997 self.result = os.EX_OSFILE
1998 raise EnvironmentError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001999 "executable '%s' is not found or executable." % executable
2000 )
2001 self.logger.debug(
2002 "Running executable '{app}': '{cmd}'".format(
2003 app=self.app_name, cmd=" ".join(self.args)
2004 )
2005 )
Neale Ranns812ed392017-10-16 04:20:13 -07002006 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05002007 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07002008 env["CK_LOG_FILE_NAME"] = "-"
2009 self.process = subprocess.Popen(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002010 ["stdbuf", "-o0", "-e0"] + self.args,
2011 shell=False,
2012 env=env,
2013 preexec_fn=os.setpgrp,
2014 stdout=subprocess.PIPE,
2015 stderr=subprocess.PIPE,
2016 )
Dave Wallace24564332019-10-21 02:53:14 +00002017 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07002018 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00002019 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07002020 self.logger.info("Return code is `%s'" % self.process.returncode)
2021 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002022 self.logger.info(
2023 "Executable `{app}' wrote to stdout:".format(app=self.app_name)
2024 )
Neale Ranns812ed392017-10-16 04:20:13 -07002025 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002026 self.logger.info(out.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002027 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002028 self.logger.info(
2029 "Executable `{app}' wrote to stderr:".format(app=self.app_name)
2030 )
Neale Ranns812ed392017-10-16 04:20:13 -07002031 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002032 self.logger.info(err.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002033 self.logger.info(single_line_delim)
2034 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002035
Klement Sekera6aa58b72019-05-16 14:34:55 +02002036
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002037if __name__ == "__main__":
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002038 pass