blob: d130855bb55e689fdd8f9ff97f274531d88e7cac [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05004import logging
Paul Vinciguerra72f00042018-11-25 11:05:13 -08005import sys
Ole Trøan162989e2018-11-26 10:27:50 +00006import os
7import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04008import signal
Paul Vinciguerrad6f22172020-12-05 22:39:14 +00009import subprocess
Ole Trøan162989e2018-11-26 10:27:50 +000010import unittest
Klement Sekerab23ffd72021-05-31 16:08:53 +020011import re
Klement Sekera277b89c2016-10-28 13:20:27 +020012import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080013import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000014import random
15import copy
juraj.linkes68ebc832018-11-29 09:37:08 +010016import platform
Klement Sekerab23ffd72021-05-31 16:08:53 +020017import shutil
Ole Trøan162989e2018-11-26 10:27:50 +000018from collections import deque
19from threading import Thread, Event
20from inspect import getdoc, isclass
21from traceback import format_exception
22from logging import FileHandler, DEBUG, Formatter
Andrew Yourtchenko06f32812021-01-14 10:19:08 +000023from enum import Enum
Klement Sekera558ceab2021-04-08 19:37:41 +020024from abc import ABC, abstractmethod
Ray Kinsellab8165b92021-09-22 11:24:06 +010025from struct import pack, unpack
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070026
27import scapy.compat
Klement Sekera56c492a2022-01-10 21:57:27 +000028from scapy.packet import Raw, Packet
Klement Sekerab23ffd72021-05-31 16:08:53 +020029from config import config, available_cpus, num_cpus, max_vpp_cpus
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040030import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080031from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010032from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000033from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070034from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000035from vpp_papi_provider import VppPapiProvider
Neale Ranns6197cb72021-06-03 14:43:21 +000036from vpp_papi import VppEnum
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050037import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000038from vpp_papi.vpp_stats import VPPStats
Ole Troan4376ab22021-03-03 10:40:05 +010039from vpp_papi.vpp_transport_socket import VppTransportSocketIOError
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020040from log import (
41 RED,
42 GREEN,
43 YELLOW,
44 double_line_delim,
45 single_line_delim,
46 get_logger,
47 colorize,
48)
Ole Trøan162989e2018-11-26 10:27:50 +000049from vpp_object import VppObjectRegistry
50from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020051from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
52from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
53from scapy.layers.inet6 import ICMPv6EchoReply
Naveen Joyc872cec2022-08-30 13:59:03 -070054from vpp_running import use_running
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080055
Klement Sekera558ceab2021-04-08 19:37:41 +020056
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050057logger = logging.getLogger(__name__)
58
59# Set up an empty logger for the testcase that can be overridden as necessary
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020060null_logger = logging.getLogger("VppTestCase")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050061null_logger.addHandler(logging.NullHandler())
62
juraj.linkescae64f82018-09-19 15:01:47 +020063PASS = 0
64FAIL = 1
65ERROR = 2
66SKIP = 3
67TEST_RUN = 4
Klement Sekera558ceab2021-04-08 19:37:41 +020068SKIP_CPU_SHORTAGE = 5
juraj.linkescae64f82018-09-19 15:01:47 +020069
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040070
Klement Sekerab23ffd72021-05-31 16:08:53 +020071if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010072 import debug_internal
73
Klement Sekeraf62ae122016-10-11 11:47:09 +020074"""
75 Test framework module.
76
77 The module provides a set of tools for constructing and running tests and
78 representing the results.
79"""
80
Klement Sekeraf62ae122016-10-11 11:47:09 +020081
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040082class VppDiedError(Exception):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020083 """exception for reporting that the subprocess has died."""
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040084
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020085 signals_by_value = {
86 v: k
87 for k, v in signal.__dict__.items()
88 if k.startswith("SIG") and not k.startswith("SIG_")
89 }
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040090
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040091 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040092 self.rv = rv
93 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040094 self.testcase = testcase
95 self.method_name = method_name
96
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040097 try:
98 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -040099 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400100 pass
101
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400102 if testcase is None and method_name is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200103 in_msg = ""
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400104 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200105 in_msg = " while running %s.%s" % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400106
Klement Sekera79a31db2021-03-12 18:16:10 +0100107 if self.rv:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200108 msg = "VPP subprocess died unexpectedly%s with return code: %d%s." % (
109 in_msg,
110 self.rv,
111 " [%s]" % (self.signal_name if self.signal_name is not None else ""),
112 )
Klement Sekera79a31db2021-03-12 18:16:10 +0100113 else:
114 msg = "VPP subprocess died unexpectedly%s." % in_msg
115
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400116 super(VppDiedError, self).__init__(msg)
117
118
Damjan Marionf56b77a2016-10-03 19:44:57 +0200119class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200120 """Private class to create packet info object.
121
122 Help process information about the next packet.
123 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200124 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200125
Matej Klotton86d87c42016-11-11 11:38:55 +0100126 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200127 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100128 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200129 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100130 #: Store the index of the destination packet generator interface
131 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200132 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100133 #: Store expected ip version
134 ip = -1
135 #: Store expected upper protocol
136 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100137 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200138 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200139
Matej Klotton16a14cd2016-12-07 15:09:13 +0100140 def __eq__(self, other):
141 index = self.index == other.index
142 src = self.src == other.src
143 dst = self.dst == other.dst
144 data = self.data == other.data
145 return index and src and dst and data
146
Klement Sekeraf62ae122016-10-11 11:47:09 +0200147
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100148def pump_output(testclass):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200149 """pump output from vpp stdout/stderr to proper queues"""
Dave Wallace670724c2022-09-20 21:52:18 -0400150 if not hasattr(testclass, "vpp"):
151 return
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100152 stdout_fragment = ""
153 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400154 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200155 readable = select.select(
156 [
157 testclass.vpp.stdout.fileno(),
158 testclass.vpp.stderr.fileno(),
159 testclass.pump_thread_wakeup_pipe[0],
160 ],
161 [],
162 [],
163 )[0]
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100164 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100165 read = os.read(testclass.vpp.stdout.fileno(), 102400)
166 if len(read) > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200167 split = read.decode("ascii", errors="backslashreplace").splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100168 if len(stdout_fragment) > 0:
169 split[0] = "%s%s" % (stdout_fragment, split[0])
170 if len(split) > 0 and split[-1].endswith("\n"):
171 limit = None
172 else:
173 limit = -1
174 stdout_fragment = split[-1]
175 testclass.vpp_stdout_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200176 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100177 for line in split[:limit]:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200178 testclass.logger.info("VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100179 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100180 read = os.read(testclass.vpp.stderr.fileno(), 102400)
181 if len(read) > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200182 split = read.decode("ascii", errors="backslashreplace").splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100183 if len(stderr_fragment) > 0:
184 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200185 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100186 limit = None
187 else:
188 limit = -1
189 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200190
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100191 testclass.vpp_stderr_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200192 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100193 for line in split[:limit]:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200194 testclass.logger.error("VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800195 # ignoring the dummy pipe here intentionally - the
196 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200197
198
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800199def _is_platform_aarch64():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200200 return platform.machine() == "aarch64"
juraj.linkes68ebc832018-11-29 09:37:08 +0100201
Klement Sekera6aa58b72019-05-16 14:34:55 +0200202
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800203is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100204
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800205
Dave Wallacee95b2462022-09-18 22:28:44 -0400206def _is_distro_ubuntu2204():
207 with open("/etc/os-release") as f:
208 for line in f.readlines():
209 if "jammy" in line:
210 return True
211 return False
212
213
214is_distro_ubuntu2204 = _is_distro_ubuntu2204()
215
216
Dave Wallace670724c2022-09-20 21:52:18 -0400217def _is_distro_debian11():
218 with open("/etc/os-release") as f:
219 for line in f.readlines():
220 if "bullseye" in line:
221 return True
222 return False
223
224
225is_distro_debian11 = _is_distro_debian11()
226
227
Klement Sekera909a6a12017-08-08 04:33:53 +0200228class KeepAliveReporter(object):
229 """
230 Singleton object which reports test start to parent process
231 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200232
Klement Sekera909a6a12017-08-08 04:33:53 +0200233 _shared_state = {}
234
235 def __init__(self):
236 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800237 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200238
239 @property
240 def pipe(self):
241 return self._pipe
242
243 @pipe.setter
244 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800245 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200246 raise Exception("Internal error - pipe should only be set once.")
247 self._pipe = pipe
248
juraj.linkes40dd73b2018-09-21 13:55:16 +0200249 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200250 """
251 Write current test tmpdir & desc to keep-alive pipe to signal liveness
252 """
Dave Wallace670724c2022-09-20 21:52:18 -0400253 if not hasattr(test, "vpp") or self.pipe is None:
Klement Sekera3f6ff192017-08-11 06:56:05 +0200254 # if not running forked..
255 return
256
Klement Sekera909a6a12017-08-08 04:33:53 +0200257 if isclass(test):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200258 desc = "%s (%s)" % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200259 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200260 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200261
Klement Sekerab23ffd72021-05-31 16:08:53 +0200262 self.pipe.send((desc, config.vpp, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200263
264
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000265class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000266 # marks the suites that must run at the end
267 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000268 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000269 # marks the suites broken on VPP multi-worker
270 FIXME_VPP_WORKERS = 2
Naveen Joy6eaeea92021-09-09 17:57:02 -0700271 # marks the suites broken when ASan is enabled
272 FIXME_ASAN = 3
Dave Wallacee95b2462022-09-18 22:28:44 -0400273 # marks suites broken on Ubuntu-22.04
274 FIXME_UBUNTU2204 = 4
Dave Wallace670724c2022-09-20 21:52:18 -0400275 # marks suites broken on Debian-11
276 FIXME_DEBIAN11 = 5
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000277
278
279def create_tag_decorator(e):
280 def decorator(cls):
281 try:
282 cls.test_tags.append(e)
283 except AttributeError:
284 cls.test_tags = [e]
285 return cls
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200286
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000287 return decorator
288
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000289
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000290tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000291tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Naveen Joy6eaeea92021-09-09 17:57:02 -0700292tag_fixme_asan = create_tag_decorator(TestCaseTag.FIXME_ASAN)
Dave Wallacee95b2462022-09-18 22:28:44 -0400293tag_fixme_ubuntu2204 = create_tag_decorator(TestCaseTag.FIXME_UBUNTU2204)
Dave Wallace670724c2022-09-20 21:52:18 -0400294tag_fixme_debian11 = create_tag_decorator(TestCaseTag.FIXME_DEBIAN11)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000295
296
Klement Sekerae2636852021-03-16 12:52:12 +0100297class DummyVpp:
298 returncode = None
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200299 pid = 0xCAFEBAFE
Klement Sekerae2636852021-03-16 12:52:12 +0100300
301 def poll(self):
302 pass
303
304 def terminate(self):
305 pass
306
307
Klement Sekera558ceab2021-04-08 19:37:41 +0200308class CPUInterface(ABC):
309 cpus = []
310 skipped_due_to_cpu_lack = False
311
312 @classmethod
313 @abstractmethod
314 def get_cpus_required(cls):
315 pass
316
317 @classmethod
318 def assign_cpus(cls, cpus):
319 cls.cpus = cpus
320
321
Naveen Joyc872cec2022-08-30 13:59:03 -0700322@use_running
Klement Sekera558ceab2021-04-08 19:37:41 +0200323class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100324 """This subclass is a base class for VPP test cases that are implemented as
325 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200326 """
327
Arthur de Kerhordb023802021-03-11 10:26:54 -0800328 extra_vpp_statseg_config = ""
Ole Troana45dc072018-12-21 16:04:22 +0100329 extra_vpp_punt_config = []
330 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500331 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400332 vapi_response_timeout = 5
Klement Sekera140af152022-02-18 10:30:51 +0000333 remove_configured_vpp_objects_on_tear_down = True
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100334
Klement Sekeraf62ae122016-10-11 11:47:09 +0200335 @property
336 def packet_infos(self):
337 """List of packet infos"""
338 return self._packet_infos
339
Klement Sekeradab231a2016-12-21 08:50:14 +0100340 @classmethod
341 def get_packet_count_for_if_idx(cls, dst_if_index):
342 """Get the number of packet info for specified destination if index"""
343 if dst_if_index in cls._packet_count_for_dst_if_idx:
344 return cls._packet_count_for_dst_if_idx[dst_if_index]
345 else:
346 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200347
348 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000349 def has_tag(cls, tag):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200350 """if the test case has a given tag - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000351 try:
352 return tag in cls.test_tags
353 except AttributeError:
354 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000355 return False
356
357 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000358 def is_tagged_run_solo(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200359 """if the test case class is timing-sensitive - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000360 return cls.has_tag(TestCaseTag.RUN_SOLO)
361
362 @classmethod
Naveen Joy6eaeea92021-09-09 17:57:02 -0700363 def skip_fixme_asan(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200364 """if @tag_fixme_asan & ASan is enabled - mark for skip"""
Naveen Joy6eaeea92021-09-09 17:57:02 -0700365 if cls.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200366 vpp_extra_cmake_args = os.environ.get("VPP_EXTRA_CMAKE_ARGS", "")
367 if "DVPP_ENABLE_SANITIZE_ADDR=ON" in vpp_extra_cmake_args:
Naveen Joy6eaeea92021-09-09 17:57:02 -0700368 cls = unittest.skip("Skipping @tag_fixme_asan tests")(cls)
369
370 @classmethod
Dave Wallacee95b2462022-09-18 22:28:44 -0400371 def skip_fixme_ubuntu2204(cls):
372 """if distro is ubuntu 22.04 and @tag_fixme_ubuntu2204 mark for skip"""
373 if cls.has_tag(TestCaseTag.FIXME_UBUNTU2204):
374 cls = unittest.skip("Skipping @tag_fixme_ubuntu2204 tests")(cls)
375
376 @classmethod
Dave Wallace670724c2022-09-20 21:52:18 -0400377 def skip_fixme_debian11(cls):
378 """if distro is Debian-11 and @tag_fixme_debian11 mark for skip"""
379 if cls.has_tag(TestCaseTag.FIXME_DEBIAN11):
380 cls = unittest.skip("Skipping @tag_fixme_debian11 tests")(cls)
381
382 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200383 def instance(cls):
384 """Return the instance of this testcase"""
385 return cls.test_instance
386
Damjan Marionf56b77a2016-10-03 19:44:57 +0200387 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200388 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000389 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200390 cls.debug_core = False
391 cls.debug_gdb = False
392 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000393 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100394 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200395 if d is None:
396 return
397 dl = d.lower()
398 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200399 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000400 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200401 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000402 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200403 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100404 elif dl == "attach":
405 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200406 else:
407 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000408 if dl == "gdb-all" or dl == "gdbserver-all":
409 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200410
Klement Sekera558ceab2021-04-08 19:37:41 +0200411 @classmethod
412 def get_vpp_worker_count(cls):
413 if not hasattr(cls, "vpp_worker_count"):
414 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
415 cls.vpp_worker_count = 0
416 else:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200417 cls.vpp_worker_count = config.vpp_worker_count
Klement Sekera558ceab2021-04-08 19:37:41 +0200418 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200419
Klement Sekera558ceab2021-04-08 19:37:41 +0200420 @classmethod
421 def get_cpus_required(cls):
422 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200423
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800424 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200425 def setUpConstants(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200426 """Set-up the test case class based on environment variables"""
Klement Sekerab23ffd72021-05-31 16:08:53 +0200427 cls.step = config.step
428 cls.plugin_path = ":".join(config.vpp_plugin_dir)
429 cls.test_plugin_path = ":".join(config.vpp_test_plugin_dir)
430 cls.extern_plugin_path = ":".join(config.extern_plugin_dir)
Ole Troana45dc072018-12-21 16:04:22 +0100431 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100432 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100433 debug_cli = "cli-listen localhost:5002"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200434 size = re.search(r"\d+[gG]", config.coredump_size)
435 if size:
436 coredump_size = f"coredump-size {config.coredump_size}".lower()
437 else:
Ole Troana45dc072018-12-21 16:04:22 +0100438 coredump_size = "coredump-size unlimited"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200439 default_variant = config.variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000440 if default_variant is not None:
Benoît Ganne09ef5922022-07-29 10:52:34 +0200441 default_variant = "default { variant %s 100 }" % default_variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000442 else:
443 default_variant = ""
444
Klement Sekerab23ffd72021-05-31 16:08:53 +0200445 api_fuzzing = config.api_fuzz
Dave Barach77841402020-04-29 17:04:10 -0400446 if api_fuzzing is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200447 api_fuzzing = "off"
Dave Barach77841402020-04-29 17:04:10 -0400448
Klement Sekera8d815022021-03-15 16:58:10 +0100449 cls.vpp_cmdline = [
Klement Sekerab23ffd72021-05-31 16:08:53 +0200450 config.vpp,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200451 "unix",
452 "{",
453 "nodaemon",
454 debug_cli,
455 "full-coredump",
456 coredump_size,
457 "runtime-dir",
458 cls.tempdir,
Klement Sekera8d815022021-03-15 16:58:10 +0100459 "}",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200460 "api-trace",
461 "{",
462 "on",
463 "}",
464 "api-segment",
465 "{",
466 "prefix",
467 cls.get_api_segment_prefix(),
468 "}",
469 "cpu",
470 "{",
471 "main-core",
472 str(cls.cpus[0]),
473 ]
474 if cls.extern_plugin_path not in (None, ""):
475 cls.extra_vpp_plugin_config.append("add-path %s" % cls.extern_plugin_path)
476 if cls.get_vpp_worker_count():
477 cls.vpp_cmdline.extend(
478 ["corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])]
479 )
480 cls.vpp_cmdline.extend(
481 [
482 "}",
483 "physmem",
484 "{",
485 "max-size",
486 "32m",
487 "}",
488 "statseg",
489 "{",
490 "socket-name",
491 cls.get_stats_sock_path(),
492 cls.extra_vpp_statseg_config,
493 "}",
494 "socksvr",
495 "{",
496 "socket-name",
497 cls.get_api_sock_path(),
498 "}",
499 "node { ",
500 default_variant,
501 "}",
502 "api-fuzz {",
503 api_fuzzing,
504 "}",
505 "plugins",
506 "{",
507 "plugin",
508 "dpdk_plugin.so",
509 "{",
510 "disable",
511 "}",
512 "plugin",
513 "rdma_plugin.so",
514 "{",
515 "disable",
516 "}",
517 "plugin",
518 "lisp_unittest_plugin.so",
519 "{",
520 "enable",
521 "}",
522 "plugin",
523 "unittest_plugin.so",
524 "{",
525 "enable",
526 "}",
527 ]
528 + cls.extra_vpp_plugin_config
529 + [
530 "}",
531 ]
532 )
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000533
Ole Troana45dc072018-12-21 16:04:22 +0100534 if cls.extra_vpp_punt_config is not None:
535 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Dave Barach7d31ab22019-05-08 19:18:18 -0400536
Klement Sekerae2636852021-03-16 12:52:12 +0100537 if not cls.debug_attach:
538 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
539 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200540
541 @classmethod
542 def wait_for_enter(cls):
543 if cls.debug_gdbserver:
544 print(double_line_delim)
545 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
546 elif cls.debug_gdb:
547 print(double_line_delim)
548 print("Spawned VPP with PID: %d" % cls.vpp.pid)
549 else:
550 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
551 return
552 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000553 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200554 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200555 print(
556 f"sudo gdb {config.vpp} "
557 f"-ex 'target remote localhost:{cls.gdbserver_port}'"
558 )
559 print(
560 "Now is the time to attach gdb by running the above "
561 "command, set up breakpoints etc., then resume VPP from "
562 "within gdb by issuing the 'continue' command"
563 )
Dave Wallace24564332019-10-21 02:53:14 +0000564 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200565 elif cls.debug_gdb:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200566 print(f"sudo gdb {config.vpp} -ex 'attach {cls.vpp.pid}'")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200567 print(
568 "Now is the time to attach gdb by running the above "
569 "command and set up breakpoints etc., then resume VPP from"
570 " within gdb by issuing the 'continue' command"
571 )
Klement Sekera277b89c2016-10-28 13:20:27 +0200572 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800573 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200574
575 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100576 def attach_vpp(cls):
577 cls.vpp = DummyVpp()
578
579 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200580 def run_vpp(cls):
Dave Wallace670724c2022-09-20 21:52:18 -0400581 if (
582 is_distro_ubuntu2204 == True and cls.has_tag(TestCaseTag.FIXME_UBUNTU2204)
583 ) or (is_distro_debian11 == True and cls.has_tag(TestCaseTag.FIXME_DEBIAN11)):
584 return
Klement Sekera558ceab2021-04-08 19:37:41 +0200585 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200586 cmdline = cls.vpp_cmdline
587
588 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200589 gdbserver = "/usr/bin/gdbserver"
590 if not os.path.isfile(gdbserver) or not os.access(gdbserver, os.X_OK):
591 raise Exception(
592 "gdbserver binary '%s' does not exist or is "
593 "not executable" % gdbserver
594 )
Klement Sekera931be3a2016-11-03 05:36:01 +0100595
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200596 cmdline = [
597 gdbserver,
598 "localhost:{port}".format(port=cls.gdbserver_port),
599 ] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200600 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
601
Klement Sekera931be3a2016-11-03 05:36:01 +0100602 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200603 cls.vpp = subprocess.Popen(
604 cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE
605 )
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800606 except subprocess.CalledProcessError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200607 cls.logger.critical(
608 "Subprocess returned with non-0 return code: (%s)", e.returncode
609 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800610 raise
611 except OSError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200612 cls.logger.critical(
613 "Subprocess returned with OS error: (%s) %s", e.errno, e.strerror
614 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800615 raise
616 except Exception as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200617 cls.logger.exception("Subprocess returned unexpected from %s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100618 raise
619
Klement Sekera277b89c2016-10-28 13:20:27 +0200620 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100621
Damjan Marionf56b77a2016-10-03 19:44:57 +0200622 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000623 def wait_for_coredump(cls):
624 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400625 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000626 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400627 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000628 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400629 ok = False
630 while time.time() < deadline:
631 cls.sleep(1)
632 size = curr_size
633 curr_size = os.path.getsize(corefile)
634 if size == curr_size:
635 ok = True
636 break
637 if not ok:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200638 cls.logger.error(
639 "Timed out waiting for coredump to complete: %s", corefile
640 )
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400641 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200642 cls.logger.error("Coredump complete: %s, size %d", corefile, curr_size)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400643
644 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100645 def get_stats_sock_path(cls):
646 return "%s/stats.sock" % cls.tempdir
647
648 @classmethod
649 def get_api_sock_path(cls):
650 return "%s/api.sock" % cls.tempdir
651
652 @classmethod
653 def get_api_segment_prefix(cls):
654 return os.path.basename(cls.tempdir) # Only used for VAPI
655
656 @classmethod
657 def get_tempdir(cls):
Klement Sekerab3fc6582022-03-10 11:47:45 +0100658 if cls.debug_attach:
659 tmpdir = f"{config.tmp_dir}/unittest-attach-gdb"
660 else:
661 tmpdir = f"{config.tmp_dir}/vpp-unittest-{cls.__name__}"
662 if config.wipe_tmp_dir:
663 shutil.rmtree(tmpdir, ignore_errors=True)
664 os.mkdir(tmpdir)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200665 return tmpdir
666
667 @classmethod
668 def create_file_handler(cls):
669 if config.log_dir is None:
670 cls.file_handler = FileHandler(f"{cls.tempdir}/log.txt")
671 return
672
673 logdir = f"{config.log_dir}/vpp-unittest-{cls.__name__}"
674 if config.wipe_tmp_dir:
675 shutil.rmtree(logdir, ignore_errors=True)
676 os.mkdir(logdir)
677 cls.file_handler = FileHandler(f"{logdir}/log.txt")
Klement Sekerae2636852021-03-16 12:52:12 +0100678
679 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200680 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200681 """
682 Perform class setup before running the testcase
683 Remove shared memory files, start vpp and connect the vpp-api
684 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800685 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100686 cls.logger = get_logger(cls.__name__)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200687 random.seed(config.rnd_seed)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200688 if hasattr(cls, "parallel_handler"):
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100689 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100690 cls.logger.propagate = False
Klement Sekerab23ffd72021-05-31 16:08:53 +0200691 cls.set_debug_flags(config.debug)
Klement Sekerae2636852021-03-16 12:52:12 +0100692 cls.tempdir = cls.get_tempdir()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200693 cls.create_file_handler()
Klement Sekera027dbd52017-04-11 06:01:53 +0200694 cls.file_handler.setFormatter(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200695 Formatter(fmt="%(asctime)s,%(msecs)03d %(message)s", datefmt="%H:%M:%S")
696 )
Klement Sekera027dbd52017-04-11 06:01:53 +0200697 cls.file_handler.setLevel(DEBUG)
698 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100699 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200700 os.chdir(cls.tempdir)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200701 cls.logger.info(
702 "Temporary dir is %s, api socket is %s",
703 cls.tempdir,
704 cls.get_api_sock_path(),
705 )
Klement Sekerab23ffd72021-05-31 16:08:53 +0200706 cls.logger.debug("Random seed is %s", config.rnd_seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200707 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100708 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200709 cls._pcaps = []
710 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200711 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100712 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100713 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200714 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200715 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200716 # need to catch exceptions here because if we raise, then the cleanup
717 # doesn't get called and we might end with a zombie vpp
718 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100719 if cls.debug_attach:
720 cls.attach_vpp()
721 else:
722 cls.run_vpp()
Dave Wallace670724c2022-09-20 21:52:18 -0400723 if not hasattr(cls, "vpp"):
724 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200725 cls.reporter.send_keep_alive(cls, "setUpClass")
juraj.linkes40dd73b2018-09-21 13:55:16 +0200726 VppTestResult.current_test_case_info = TestCaseInfo(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200727 cls.logger, cls.tempdir, cls.vpp.pid, config.vpp
728 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100729 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100730 cls.vpp_stderr_deque = deque()
Naveen Joyc872cec2022-08-30 13:59:03 -0700731 # Pump thread in a non-debug-attached & not running-vpp
732 if not cls.debug_attach and not hasattr(cls, "running_vpp"):
Klement Sekerae2636852021-03-16 12:52:12 +0100733 cls.pump_thread_stop_flag = Event()
734 cls.pump_thread_wakeup_pipe = os.pipe()
735 cls.pump_thread = Thread(target=pump_output, args=(cls,))
736 cls.pump_thread.daemon = True
737 cls.pump_thread.start()
738 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400739 cls.vapi_response_timeout = 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200740 cls.vapi = VppPapiProvider(cls.__name__, cls, cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100741 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400742 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100743 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400744 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100745 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100746 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200747 try:
748 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100749 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200750 cls.vpp_startup_failed = True
751 cls.logger.critical(
752 "VPP died shortly after startup, check the"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200753 " output to standard error for possible cause"
754 )
Klement Sekera3747c752017-04-10 06:30:17 +0200755 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100756 try:
757 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100758 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500759 cls.logger.debug("Exception connecting to vapi: %s" % e)
760 cls.vapi.disconnect()
761
Klement Sekera085f5c02016-11-24 01:59:16 +0100762 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200763 print(
764 colorize(
765 "You're running VPP inside gdbserver but "
766 "VPP-API connection failed, did you forget "
767 "to 'continue' VPP from within gdb?",
768 RED,
769 )
770 )
Ole Troan4376ab22021-03-03 10:40:05 +0100771 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100772 if cls.debug_attach:
773 last_line = cls.vapi.cli("show thread").split("\n")[-2]
774 cls.vpp_worker_count = int(last_line.split(" ")[0])
775 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400776 except vpp_papi.VPPRuntimeError as e:
777 cls.logger.debug("%s" % e)
778 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100779 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000780 except Exception as e:
781 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400782 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100783 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784
Damjan Marionf56b77a2016-10-03 19:44:57 +0200785 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500786 def _debug_quit(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200787 if cls.debug_gdbserver or cls.debug_gdb:
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500788 try:
789 cls.vpp.poll()
790
791 if cls.vpp.returncode is None:
792 print()
793 print(double_line_delim)
794 print("VPP or GDB server is still running")
795 print(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200796 input(
797 "When done debugging, press ENTER to kill the "
798 "process and finish running the testcase..."
799 )
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500800 except AttributeError:
801 pass
802
803 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200804 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200805 """
806 Disconnect vpp-api, kill vpp and cleanup shared memory files
807 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500808 cls._debug_quit()
Naveen Joyc872cec2022-08-30 13:59:03 -0700809 if hasattr(cls, "running_vpp"):
810 cls.vpp.quit_vpp()
Klement Sekera277b89c2016-10-28 13:20:27 +0200811
juraj.linkes184870a2018-07-16 14:22:01 +0200812 # first signal that we want to stop the pump thread, then wake it up
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200813 if hasattr(cls, "pump_thread_stop_flag"):
juraj.linkes184870a2018-07-16 14:22:01 +0200814 cls.pump_thread_stop_flag.set()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200815 if hasattr(cls, "pump_thread_wakeup_pipe"):
816 os.write(cls.pump_thread_wakeup_pipe[1], b"ding dong wake up")
817 if hasattr(cls, "pump_thread"):
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100818 cls.logger.debug("Waiting for pump thread to stop")
819 cls.pump_thread.join()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200820 if hasattr(cls, "vpp_stderr_reader_thread"):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500821 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100822 cls.vpp_stderr_reader_thread.join()
823
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200824 if hasattr(cls, "vpp"):
825 if hasattr(cls, "vapi"):
Ole Troanfd574082019-11-27 23:12:48 +0100826 cls.logger.debug(cls.vapi.vpp.get_stats())
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200827 cls.logger.debug("Disconnecting class vapi client on %s", cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100828 cls.vapi.disconnect()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200829 cls.logger.debug("Deleting class vapi attribute on %s", cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100830 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200831 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100832 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000833 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100834 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400835 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100836 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100837 try:
838 outs, errs = cls.vpp.communicate(timeout=5)
839 except subprocess.TimeoutExpired:
840 cls.vpp.kill()
841 outs, errs = cls.vpp.communicate()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200842 cls.logger.debug("Deleting class vpp attribute on %s", cls.__name__)
Naveen Joyc872cec2022-08-30 13:59:03 -0700843 if not cls.debug_attach and not hasattr(cls, "running_vpp"):
Klement Sekerae2636852021-03-16 12:52:12 +0100844 cls.vpp.stdout.close()
845 cls.vpp.stderr.close()
Naveen Joyc872cec2022-08-30 13:59:03 -0700846 # If vpp is a dynamic attribute set by the func use_running,
847 # deletion will result in an AttributeError that we can
848 # safetly pass.
849 try:
850 del cls.vpp
851 except AttributeError:
852 pass
Damjan Marionf56b77a2016-10-03 19:44:57 +0200853
Klement Sekera3747c752017-04-10 06:30:17 +0200854 if cls.vpp_startup_failed:
855 stdout_log = cls.logger.info
856 stderr_log = cls.logger.critical
857 else:
858 stdout_log = cls.logger.info
859 stderr_log = cls.logger.info
860
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200861 if hasattr(cls, "vpp_stdout_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200862 stdout_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200863 stdout_log("VPP output to stdout while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200864 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100865 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200866 with open(cls.tempdir + "/vpp_stdout.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200867 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200868 stdout_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200869 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200870
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200871 if hasattr(cls, "vpp_stderr_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200872 stderr_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200873 stderr_log("VPP output to stderr while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200874 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100875 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200876 with open(cls.tempdir + "/vpp_stderr.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200877 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200878 stderr_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200879 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200880
Damjan Marionf56b77a2016-10-03 19:44:57 +0200881 @classmethod
882 def tearDownClass(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200883 """Perform final cleanup after running all tests in this test-case"""
884 cls.logger.debug("--- tearDownClass() for %s called ---" % cls.__name__)
Dave Wallace670724c2022-09-20 21:52:18 -0400885 if not hasattr(cls, "vpp"):
886 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200887 cls.reporter.send_keep_alive(cls, "tearDownClass")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200888 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200889 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100890 cls.reset_packet_infos()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200891 if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +0100892 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200893
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700894 def show_commands_at_teardown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200895 """Allow subclass specific teardown logging additions."""
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700896 self.logger.info("--- No test specific show commands provided. ---")
897
Damjan Marionf56b77a2016-10-03 19:44:57 +0200898 def tearDown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200899 """Show various debug prints after each test"""
900 self.logger.debug(
901 "--- tearDown() for %s.%s(%s) called ---"
902 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
903 )
Dave Wallace670724c2022-09-20 21:52:18 -0400904 if not hasattr(self, "vpp"):
905 return
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700906
907 try:
908 if not self.vpp_dead:
909 self.logger.debug(self.vapi.cli("show trace max 1000"))
910 self.logger.info(self.vapi.ppcli("show interface"))
911 self.logger.info(self.vapi.ppcli("show hardware"))
912 self.logger.info(self.statistics.set_errors_str())
913 self.logger.info(self.vapi.ppcli("show run"))
914 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400915 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700916 self.logger.info("Logging testcase specific show commands.")
917 self.show_commands_at_teardown()
Klement Sekera140af152022-02-18 10:30:51 +0000918 if self.remove_configured_vpp_objects_on_tear_down:
919 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500920 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000921 m = self._testMethodName
922 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500923 tmp_api_trace = "/tmp/%s" % api_trace
924 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
925 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200926 self.logger.info("Moving %s to %s\n" % (tmp_api_trace, vpp_api_trace_log))
Dave Wallace90c55722017-02-16 11:25:26 -0500927 os.rename(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100928 except VppTransportSocketIOError:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200929 self.logger.debug(
930 "VppTransportSocketIOError: Vpp dead. Cannot log show commands."
931 )
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700932 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100933 else:
934 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200935
Damjan Marionf56b77a2016-10-03 19:44:57 +0200936 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200937 """Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800938 super(VppTestCase, self).setUp()
Dave Wallace670724c2022-09-20 21:52:18 -0400939 if not hasattr(self, "vpp"):
940 return
Klement Sekera909a6a12017-08-08 04:33:53 +0200941 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100942 if self.vpp_dead:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200943 raise VppDiedError(
944 rv=None,
945 testcase=self.__class__.__name__,
946 method_name=self._testMethodName,
947 )
948 self.sleep(0.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100949 self.vpp_stdout_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200950 "--- test setUp() for %s.%s(%s) starts here ---\n"
951 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
952 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100953 self.vpp_stderr_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200954 "--- test setUp() for %s.%s(%s) starts here ---\n"
955 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
956 )
Klement Sekeraf62ae122016-10-11 11:47:09 +0200957 self.vapi.cli("clear trace")
958 # store the test instance inside the test class - so that objects
959 # holding the class can access instance methods (like assertEqual)
960 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200961
Damjan Marionf56b77a2016-10-03 19:44:57 +0200962 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200963 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200964 """
965 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200966
Klement Sekera75e7d132017-09-20 08:26:30 +0200967 :param interfaces: iterable interface indexes (if None,
968 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200969
Klement Sekeraf62ae122016-10-11 11:47:09 +0200970 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200971 if interfaces is None:
972 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200973 for i in interfaces:
974 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200975
Damjan Marionf56b77a2016-10-03 19:44:57 +0200976 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200977 def register_pcap(cls, intf, worker):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200978 """Register a pcap in the testclass"""
Klement Sekera9225dee2016-12-12 08:36:58 +0100979 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200980 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100981
982 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000983 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400984 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
985 # returns float("2.190522")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200986 timestr = cls.vapi.cli("show clock")
987 head, sep, tail = timestr.partition(",")
988 head, sep, tail = head.partition("Time now")
Dave Barach19718002020-03-11 10:31:36 -0400989 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000990
991 @classmethod
992 def sleep_on_vpp_time(cls, sec):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200993 """Sleep according to time in VPP world"""
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000994 # On a busy system with many processes
995 # we might end up with VPP time being slower than real world
996 # So take that into account when waiting for VPP to do something
997 start_time = cls.get_vpp_time()
998 while cls.get_vpp_time() - start_time < sec:
999 cls.sleep(0.1)
1000
1001 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +01001002 def pg_start(cls, trace=True):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001003 """Enable the PG, wait till it is done, then clean up"""
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001004 for (intf, worker) in cls._old_pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001005 intf.handle_old_pcap_file(intf.get_in_path(worker), intf.in_history_counter)
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001006 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +01001007 if trace:
1008 cls.vapi.cli("clear trace")
1009 cls.vapi.cli("trace add pg-input 1000")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001010 cls.vapi.cli("packet-generator enable")
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +00001011 # PG, when starts, runs to completion -
1012 # so let's avoid a race condition,
1013 # and wait a little till it's done.
1014 # Then clean it up - and then be gone.
1015 deadline = time.time() + 300
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001016 while cls.vapi.cli("show packet-generator").find("Yes") != -1:
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +00001017 cls.sleep(0.01) # yield
1018 if time.time() > deadline:
1019 cls.logger.error("Timeout waiting for pg to stop")
1020 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001021 for intf, worker in cls._pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001022 cls.vapi.cli("packet-generator delete %s" % intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001023 cls._old_pcaps = cls._pcaps
1024 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001025
Damjan Marionf56b77a2016-10-03 19:44:57 +02001026 @classmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001027 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0, mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001028 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001029 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +02001030
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001031 :param interfaces: iterable indexes of the interfaces.
1032 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +02001033
Klement Sekeraf62ae122016-10-11 11:47:09 +02001034 """
1035 result = []
1036 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +00001037 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001038 setattr(cls, intf.name, intf)
1039 result.append(intf)
1040 cls.pg_interfaces = result
1041 return result
1042
Matej Klotton0178d522016-11-04 11:11:44 +01001043 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +00001044 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001045 if not hasattr(cls, "vpp"):
1046 cls.pg_interfaces = []
1047 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001048 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001049 return cls.create_pg_interfaces_internal(
1050 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP4
1051 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001052
1053 @classmethod
1054 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001055 if not hasattr(cls, "vpp"):
1056 cls.pg_interfaces = []
1057 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001058 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001059 return cls.create_pg_interfaces_internal(
1060 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP6
1061 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001062
1063 @classmethod
1064 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001065 if not hasattr(cls, "vpp"):
1066 cls.pg_interfaces = []
1067 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001068 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001069 return cls.create_pg_interfaces_internal(
1070 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1071 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001072
1073 @classmethod
1074 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001075 if not hasattr(cls, "vpp"):
1076 cls.pg_interfaces = []
1077 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001078 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001079 return cls.create_pg_interfaces_internal(
1080 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1081 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001082
1083 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +02001084 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +01001085 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001086 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001087
Klement Sekerab9ef2732018-06-24 22:49:33 +02001088 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001089 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001090 """
Dave Wallace670724c2022-09-20 21:52:18 -04001091 if not hasattr(cls, "vpp"):
1092 cls.lo_interfaces = []
1093 return cls.lo_interfaces
Klement Sekerab9ef2732018-06-24 22:49:33 +02001094 result = [VppLoInterface(cls) for i in range(count)]
1095 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +01001096 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +01001097 cls.lo_interfaces = result
1098 return result
1099
Neale Ranns192b13f2019-03-15 02:16:20 -07001100 @classmethod
1101 def create_bvi_interfaces(cls, count):
1102 """
1103 Create BVI interfaces.
1104
1105 :param count: number of interfaces created.
1106 :returns: List of created interfaces.
1107 """
Dave Wallace670724c2022-09-20 21:52:18 -04001108 if not hasattr(cls, "vpp"):
1109 cls.bvi_interfaces = []
1110 return cls.bvi_interfaces
Neale Ranns192b13f2019-03-15 02:16:20 -07001111 result = [VppBviInterface(cls) for i in range(count)]
1112 for intf in result:
1113 setattr(cls, intf.name, intf)
1114 cls.bvi_interfaces = result
1115 return result
1116
Damjan Marionf56b77a2016-10-03 19:44:57 +02001117 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001118 def extend_packet(packet, size, padding=" "):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001119 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001120 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +02001121 NOTE: Currently works only when Raw layer is present.
1122
1123 :param packet: packet
1124 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +02001125 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +02001126
1127 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001128 packet_len = len(packet) + 4
1129 extend = size - packet_len
1130 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +02001131 num = (extend // len(padding)) + 1
1132 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +02001133
Klement Sekeradab231a2016-12-21 08:50:14 +01001134 @classmethod
1135 def reset_packet_infos(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001136 """Reset the list of packet info objects and packet counts to zero"""
Klement Sekeradab231a2016-12-21 08:50:14 +01001137 cls._packet_infos = {}
1138 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +02001139
Klement Sekeradab231a2016-12-21 08:50:14 +01001140 @classmethod
1141 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001142 """
1143 Create packet info object containing the source and destination indexes
1144 and add it to the testcase's packet info list
1145
Klement Sekeradab231a2016-12-21 08:50:14 +01001146 :param VppInterface src_if: source interface
1147 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +02001148
1149 :returns: _PacketInfo object
1150
1151 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001152 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +01001153 info.index = len(cls._packet_infos)
1154 info.src = src_if.sw_if_index
1155 info.dst = dst_if.sw_if_index
1156 if isinstance(dst_if, VppSubInterface):
1157 dst_idx = dst_if.parent.sw_if_index
1158 else:
1159 dst_idx = dst_if.sw_if_index
1160 if dst_idx in cls._packet_count_for_dst_if_idx:
1161 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1162 else:
1163 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1164 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001165 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001166
Damjan Marionf56b77a2016-10-03 19:44:57 +02001167 @staticmethod
1168 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001169 """
1170 Convert _PacketInfo object to packet payload
1171
1172 :param info: _PacketInfo object
1173
1174 :returns: string containing serialized data from packet info
1175 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001176
1177 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001178 return pack("iiiih", info.index, info.src, info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001179
Damjan Marionf56b77a2016-10-03 19:44:57 +02001180 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001181 def payload_to_info(payload, payload_field="load"):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001182 """
1183 Convert packet payload to _PacketInfo object
1184
1185 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001186 :type payload: <class 'scapy.packet.Raw'>
1187 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001188 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001189 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001190 :returns: _PacketInfo object containing de-serialized data from payload
1191
1192 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001193
1194 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1195 payload_b = getattr(payload, payload_field)[:18]
1196
Damjan Marionf56b77a2016-10-03 19:44:57 +02001197 info = _PacketInfo()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001198 info.index, info.src, info.dst, info.ip, info.proto = unpack("iiiih", payload_b)
Ray Kinsellab8165b92021-09-22 11:24:06 +01001199
1200 # some SRv6 TCs depend on get an exception if bad values are detected
1201 if info.index > 0x4000:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001202 raise ValueError("Index value is invalid")
Ray Kinsellab8165b92021-09-22 11:24:06 +01001203
Damjan Marionf56b77a2016-10-03 19:44:57 +02001204 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001205
Damjan Marionf56b77a2016-10-03 19:44:57 +02001206 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001207 """
1208 Iterate over the packet info list stored in the testcase
1209 Start iteration with first element if info is None
1210 Continue based on index in info if info is specified
1211
1212 :param info: info or None
1213 :returns: next info in list or None if no more infos
1214 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001215 if info is None:
1216 next_index = 0
1217 else:
1218 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001219 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001220 return None
1221 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001222 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001223
Klement Sekeraf62ae122016-10-11 11:47:09 +02001224 def get_next_packet_info_for_interface(self, src_index, info):
1225 """
1226 Search the packet info list for the next packet info with same source
1227 interface index
1228
1229 :param src_index: source interface index to search for
1230 :param info: packet info - where to start the search
1231 :returns: packet info or None
1232
1233 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001234 while True:
1235 info = self.get_next_packet_info(info)
1236 if info is None:
1237 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001238 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001239 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001240
Klement Sekeraf62ae122016-10-11 11:47:09 +02001241 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1242 """
1243 Search the packet info list for the next packet info with same source
1244 and destination interface indexes
1245
1246 :param src_index: source interface index to search for
1247 :param dst_index: destination interface index to search for
1248 :param info: packet info - where to start the search
1249 :returns: packet info or None
1250
1251 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001252 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001253 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001254 if info is None:
1255 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001256 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001257 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001258
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001259 def assert_equal(self, real_value, expected_value, name_or_class=None):
1260 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001261 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001262 return
1263 try:
1264 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001265 msg = msg % (
1266 getdoc(name_or_class).strip(),
1267 real_value,
1268 str(name_or_class(real_value)),
1269 expected_value,
1270 str(name_or_class(expected_value)),
1271 )
Klement Sekera13a83ef2018-03-21 12:35:51 +01001272 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001273 msg = "Invalid %s: %s does not match expected value %s" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001274 name_or_class,
1275 real_value,
1276 expected_value,
1277 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001278
1279 self.assertEqual(real_value, expected_value, msg)
1280
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001281 def assert_in_range(self, real_value, expected_min, expected_max, name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001282 if name is None:
1283 msg = None
1284 else:
1285 msg = "Invalid %s: %s out of range <%s,%s>" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001286 name,
1287 real_value,
1288 expected_min,
1289 expected_max,
1290 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001291 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1292
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001293 def assert_packet_checksums_valid(self, packet, ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001294 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001295 udp_layers = ["UDP", "UDPerror"]
1296 checksum_fields = ["cksum", "chksum"]
Klement Sekerad81ae412018-05-16 10:52:54 +02001297 checksums = []
1298 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001299 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001300 while True:
1301 layer = temp.getlayer(counter)
1302 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001303 layer = layer.copy()
1304 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001305 for cf in checksum_fields:
1306 if hasattr(layer, cf):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001307 if (
1308 ignore_zero_udp_checksums
1309 and 0 == getattr(layer, cf)
1310 and layer.name in udp_layers
1311 ):
Klement Sekerad81ae412018-05-16 10:52:54 +02001312 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001313 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001314 checksums.append((counter, cf))
1315 else:
1316 break
1317 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001318 if 0 == len(checksums):
1319 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001320 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001321 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001322 calc_sum = getattr(temp[layer], cf)
1323 self.assert_equal(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001324 getattr(received[layer], cf),
1325 calc_sum,
1326 "packet checksum on layer #%d: %s" % (layer, temp[layer].name),
1327 )
Klement Sekera31da2e32018-06-24 22:49:55 +02001328 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001329 "Checksum field `%s` on `%s` layer has correct value `%s`"
1330 % (cf, temp[layer].name, calc_sum)
1331 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001332
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001333 def assert_checksum_valid(
1334 self, received_packet, layer, field_name="chksum", ignore_zero_checksum=False
1335 ):
1336 """Check checksum of received packet on given layer"""
Klement Sekerad81ae412018-05-16 10:52:54 +02001337 received_packet_checksum = getattr(received_packet[layer], field_name)
1338 if ignore_zero_checksum and 0 == received_packet_checksum:
1339 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001340 recalculated = received_packet.__class__(scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001341 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001342 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001343 self.assert_equal(
1344 received_packet_checksum,
1345 getattr(recalculated[layer], field_name),
1346 "packet checksum on layer: %s" % layer,
1347 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001348
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001349 def assert_ip_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1350 self.assert_checksum_valid(
1351 received_packet, "IP", ignore_zero_checksum=ignore_zero_checksum
1352 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001353
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001354 def assert_tcp_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1355 self.assert_checksum_valid(
1356 received_packet, "TCP", ignore_zero_checksum=ignore_zero_checksum
1357 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001358
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001359 def assert_udp_checksum_valid(self, received_packet, ignore_zero_checksum=True):
1360 self.assert_checksum_valid(
1361 received_packet, "UDP", ignore_zero_checksum=ignore_zero_checksum
1362 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001363
1364 def assert_embedded_icmp_checksum_valid(self, received_packet):
1365 if received_packet.haslayer(IPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001366 self.assert_checksum_valid(received_packet, "IPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001367 if received_packet.haslayer(TCPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001368 self.assert_checksum_valid(received_packet, "TCPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001369 if received_packet.haslayer(UDPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001370 self.assert_checksum_valid(
1371 received_packet, "UDPerror", ignore_zero_checksum=True
1372 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001373 if received_packet.haslayer(ICMPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001374 self.assert_checksum_valid(received_packet, "ICMPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001375
1376 def assert_icmp_checksum_valid(self, received_packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001377 self.assert_checksum_valid(received_packet, "ICMP")
Klement Sekerad81ae412018-05-16 10:52:54 +02001378 self.assert_embedded_icmp_checksum_valid(received_packet)
1379
1380 def assert_icmpv6_checksum_valid(self, pkt):
1381 if pkt.haslayer(ICMPv6DestUnreach):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001382 self.assert_checksum_valid(pkt, "ICMPv6DestUnreach", "cksum")
Klement Sekerad81ae412018-05-16 10:52:54 +02001383 self.assert_embedded_icmp_checksum_valid(pkt)
1384 if pkt.haslayer(ICMPv6EchoRequest):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001385 self.assert_checksum_valid(pkt, "ICMPv6EchoRequest", "cksum")
Klement Sekerad81ae412018-05-16 10:52:54 +02001386 if pkt.haslayer(ICMPv6EchoReply):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001387 self.assert_checksum_valid(pkt, "ICMPv6EchoReply", "cksum")
Klement Sekerad81ae412018-05-16 10:52:54 +02001388
Klement Sekera107ad732022-02-18 10:32:08 +00001389 def get_counter(self, counter):
Klement Sekera3a343d42019-05-16 14:35:46 +02001390 if counter.startswith("/"):
1391 counter_value = self.statistics.get_counter(counter)
1392 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001393 counters = self.vapi.cli("sh errors").split("\n")
Klement Sekera6aa58b72019-05-16 14:34:55 +02001394 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001395 for i in range(1, len(counters) - 1):
1396 results = counters[i].split()
1397 if results[1] == counter:
1398 counter_value = int(results[0])
1399 break
1400 return counter_value
1401
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001402 def assert_counter_equal(self, counter, expected_value, thread=None, index=0):
Klement Sekera107ad732022-02-18 10:32:08 +00001403 c = self.get_counter(counter)
1404 if thread is not None:
1405 c = c[thread][index]
1406 else:
1407 c = sum(x[index] for x in c)
1408 self.assert_equal(c, expected_value, "counter `%s'" % counter)
1409
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001410 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera107ad732022-02-18 10:32:08 +00001411 counter_value = self.get_counter(counter)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001412 self.assert_equal(
1413 counter_value, expected_value, "packet counter `%s'" % counter
1414 )
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001415
Ole Troan233e4682019-05-16 15:01:34 +02001416 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001417 counter_value = self.statistics[counter].sum()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001418 self.assert_equal(counter_value, expected_value, "error counter `%s'" % counter)
Ole Troan233e4682019-05-16 15:01:34 +02001419
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001420 @classmethod
1421 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001422
1423 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1424 # * by Guido, only the main thread can be interrupted.
1425 # */
1426 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1427 if timeout == 0:
1428 # yield quantum
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001429 if hasattr(os, "sched_yield"):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001430 os.sched_yield()
1431 else:
1432 time.sleep(0)
1433 return
1434
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001435 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001436 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001437 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001438 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001439 if after - before > 2 * timeout:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001440 cls.logger.error(
1441 "unexpected self.sleep() result - slept for %es instead of ~%es!",
1442 after - before,
1443 timeout,
1444 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001445
1446 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001447 "Finished sleep (%s) - slept %es (wanted %es)",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001448 remark,
1449 after - before,
1450 timeout,
1451 )
Klement Sekeraa57a9702017-02-02 06:58:07 +01001452
Benoît Ganne56eccdb2021-08-20 09:18:31 +02001453 def virtual_sleep(self, timeout, remark=None):
1454 self.logger.debug("Moving VPP time by %s (%s)", timeout, remark)
1455 self.vapi.cli("set clock adjust %s" % timeout)
1456
Benoît Ganne8c45e512021-02-19 16:39:13 +01001457 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001458 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001459 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001460 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001461
Klement Sekeraad3187f2022-02-18 10:34:35 +00001462 def snapshot_stats(self, stats_diff):
1463 """Return snapshot of interesting stats based on diff dictionary."""
1464 stats_snapshot = {}
1465 for sw_if_index in stats_diff:
1466 for counter in stats_diff[sw_if_index]:
1467 stats_snapshot[counter] = self.statistics[counter]
1468 self.logger.debug(f"Took statistics stats_snapshot: {stats_snapshot}")
1469 return stats_snapshot
1470
1471 def compare_stats_with_snapshot(self, stats_diff, stats_snapshot):
1472 """Assert appropriate difference between current stats and snapshot."""
1473 for sw_if_index in stats_diff:
1474 for cntr, diff in stats_diff[sw_if_index].items():
1475 if sw_if_index == "err":
1476 self.assert_equal(
1477 self.statistics[cntr].sum(),
1478 stats_snapshot[cntr].sum() + diff,
1479 f"'{cntr}' counter value (previous value: "
1480 f"{stats_snapshot[cntr].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001481 f"expected diff: {diff})",
1482 )
Klement Sekeraad3187f2022-02-18 10:34:35 +00001483 else:
1484 try:
1485 self.assert_equal(
1486 self.statistics[cntr][:, sw_if_index].sum(),
1487 stats_snapshot[cntr][:, sw_if_index].sum() + diff,
1488 f"'{cntr}' counter value (previous value: "
1489 f"{stats_snapshot[cntr][:, sw_if_index].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001490 f"expected diff: {diff})",
1491 )
Klement Sekeraad3187f2022-02-18 10:34:35 +00001492 except IndexError:
1493 # if diff is 0, then this most probably a case where
1494 # test declares multiple interfaces but traffic hasn't
1495 # passed through this one yet - which means the counter
1496 # value is 0 and can be ignored
1497 if 0 != diff:
1498 raise
1499
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001500 def send_and_assert_no_replies(
1501 self, intf, pkts, remark="", timeout=None, stats_diff=None, trace=True, msg=None
1502 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001503 if stats_diff:
1504 stats_snapshot = self.snapshot_stats(stats_diff)
1505
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001506 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001507
1508 try:
1509 if not timeout:
1510 timeout = 1
1511 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +00001512 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001513 timeout = 0.1
1514 finally:
1515 if trace:
1516 if msg:
1517 self.logger.debug(f"send_and_assert_no_replies: {msg}")
1518 self.logger.debug(self.vapi.cli("show trace"))
1519
1520 if stats_diff:
1521 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -08001522
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001523 def send_and_expect(
1524 self,
1525 intf,
1526 pkts,
1527 output,
1528 n_rx=None,
1529 worker=None,
1530 trace=True,
1531 msg=None,
1532 stats_diff=None,
1533 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001534 if stats_diff:
1535 stats_snapshot = self.snapshot_stats(stats_diff)
1536
Neale Rannsd7603d92019-03-28 08:56:10 +00001537 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +00001538 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001539 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001540 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +02001541 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001542 if msg:
1543 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +02001544 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +00001545
1546 if stats_diff:
1547 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1548
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001549 return rx
1550
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001551 def send_and_expect_load_balancing(
1552 self, input, pkts, outputs, worker=None, trace=True
1553 ):
Neale Ranns699bea22022-02-17 09:22:16 +00001554 self.pg_send(input, pkts, worker=worker, trace=trace)
1555 rxs = []
1556 for oo in outputs:
1557 rx = oo._get_capture(1)
1558 self.assertNotEqual(0, len(rx))
1559 rxs.append(rx)
1560 if trace:
1561 self.logger.debug(self.vapi.cli("show trace"))
1562 return rxs
1563
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001564 def send_and_expect_some(self, intf, pkts, output, worker=None, trace=True):
Neale Ranns5c6dd172022-02-17 09:08:47 +00001565 self.pg_send(intf, pkts, worker=worker, trace=trace)
1566 rx = output._get_capture(1)
1567 if trace:
1568 self.logger.debug(self.vapi.cli("show trace"))
1569 self.assertTrue(len(rx) > 0)
1570 self.assertTrue(len(rx) < len(pkts))
1571 return rx
1572
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001573 def send_and_expect_only(self, intf, pkts, output, timeout=None, stats_diff=None):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001574 if stats_diff:
1575 stats_snapshot = self.snapshot_stats(stats_diff)
1576
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001577 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001578 rx = output.get_capture(len(pkts))
1579 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001580 if not timeout:
1581 timeout = 1
1582 for i in self.pg_interfaces:
1583 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +00001584 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001585 timeout = 0.1
1586
Klement Sekeraad3187f2022-02-18 10:34:35 +00001587 if stats_diff:
1588 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1589
Neale Ranns52fae862018-01-08 04:41:42 -08001590 return rx
1591
Damjan Marionf56b77a2016-10-03 19:44:57 +02001592
juraj.linkes184870a2018-07-16 14:22:01 +02001593def get_testcase_doc_name(test):
1594 return getdoc(test.__class__).splitlines()[0]
1595
1596
Ole Trøan5ba91592018-11-22 10:01:09 +00001597def get_test_description(descriptions, test):
1598 short_description = test.shortDescription()
1599 if descriptions and short_description:
1600 return short_description
1601 else:
1602 return str(test)
1603
1604
juraj.linkes40dd73b2018-09-21 13:55:16 +02001605class TestCaseInfo(object):
1606 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1607 self.logger = logger
1608 self.tempdir = tempdir
1609 self.vpp_pid = vpp_pid
1610 self.vpp_bin_path = vpp_bin_path
1611 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001612
1613
Damjan Marionf56b77a2016-10-03 19:44:57 +02001614class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001615 """
1616 @property result_string
1617 String variable to store the test case result string.
1618 @property errors
1619 List variable containing 2-tuples of TestCase instances and strings
1620 holding formatted tracebacks. Each tuple represents a test which
1621 raised an unexpected exception.
1622 @property failures
1623 List variable containing 2-tuples of TestCase instances and strings
1624 holding formatted tracebacks. Each tuple represents a test where
1625 a failure was explicitly signalled using the TestCase.assert*()
1626 methods.
1627 """
1628
juraj.linkes40dd73b2018-09-21 13:55:16 +02001629 failed_test_cases_info = set()
1630 core_crash_test_cases_info = set()
1631 current_test_case_info = None
1632
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001633 def __init__(self, stream=None, descriptions=None, verbosity=None, runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001634 """
Klement Sekerada505f62017-01-04 12:58:53 +01001635 :param stream File descriptor to store where to report test results.
1636 Set to the standard error stream by default.
1637 :param descriptions Boolean variable to store information if to use
1638 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001639 :param verbosity Integer variable to store required verbosity level.
1640 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001641 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001642 self.stream = stream
1643 self.descriptions = descriptions
1644 self.verbosity = verbosity
1645 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001646 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001647 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001648
Damjan Marionf56b77a2016-10-03 19:44:57 +02001649 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001650 """
1651 Record a test succeeded result
1652
1653 :param test:
1654
1655 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001656 if self.current_test_case_info:
1657 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001658 "--- addSuccess() %s.%s(%s) called"
1659 % (test.__class__.__name__, test._testMethodName, test._testMethodDoc)
1660 )
Damjan Marionf56b77a2016-10-03 19:44:57 +02001661 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001662 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001663
juraj.linkescae64f82018-09-19 15:01:47 +02001664 self.send_result_through_pipe(test, PASS)
1665
Klement Sekeraf62ae122016-10-11 11:47:09 +02001666 def addSkip(self, test, reason):
1667 """
1668 Record a test skipped.
1669
1670 :param test:
1671 :param reason:
1672
1673 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001674 if self.current_test_case_info:
1675 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001676 "--- addSkip() %s.%s(%s) called, reason is %s"
1677 % (
1678 test.__class__.__name__,
1679 test._testMethodName,
1680 test._testMethodDoc,
1681 reason,
1682 )
1683 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001684 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001685 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001686
Klement Sekera558ceab2021-04-08 19:37:41 +02001687 if reason == "not enough cpus":
1688 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1689 else:
1690 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001691
juraj.linkes40dd73b2018-09-21 13:55:16 +02001692 def symlink_failed(self):
1693 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001694 try:
Klement Sekerab23ffd72021-05-31 16:08:53 +02001695 failed_dir = config.failed_dir
juraj.linkes40dd73b2018-09-21 13:55:16 +02001696 link_path = os.path.join(
1697 failed_dir,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001698 "%s-FAILED" % os.path.basename(self.current_test_case_info.tempdir),
1699 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001700
1701 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001702 "creating a link to the failed test"
1703 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001704 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001705 "os.symlink(%s, %s)"
1706 % (self.current_test_case_info.tempdir, link_path)
1707 )
juraj.linkes184870a2018-07-16 14:22:01 +02001708 if os.path.exists(link_path):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001709 self.current_test_case_info.logger.debug("symlink already exists")
juraj.linkes184870a2018-07-16 14:22:01 +02001710 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001711 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001712
Klement Sekeraf413bef2017-08-15 07:09:02 +02001713 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001714 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001715
juraj.linkescae64f82018-09-19 15:01:47 +02001716 def send_result_through_pipe(self, test, result):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001717 if hasattr(self, "test_framework_result_pipe"):
juraj.linkescae64f82018-09-19 15:01:47 +02001718 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001719 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001720 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001721
juraj.linkes40dd73b2018-09-21 13:55:16 +02001722 def log_error(self, test, err, fn_name):
1723 if self.current_test_case_info:
1724 if isinstance(test, unittest.suite._ErrorHolder):
1725 test_name = test.description
1726 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001727 test_name = "%s.%s(%s)" % (
1728 test.__class__.__name__,
1729 test._testMethodName,
1730 test._testMethodDoc,
1731 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001732 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001733 "--- %s() %s called, err is %s" % (fn_name, test_name, err)
1734 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001735 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001736 "formatted exception is:\n%s" % "".join(format_exception(*err))
1737 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001738
1739 def add_error(self, test, err, unittest_fn, error_type):
1740 if error_type == FAIL:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001741 self.log_error(test, err, "addFailure")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001742 error_type_str = colorize("FAIL", RED)
1743 elif error_type == ERROR:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001744 self.log_error(test, err, "addError")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001745 error_type_str = colorize("ERROR", RED)
1746 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001747 raise Exception(
1748 "Error type %s cannot be used to record an "
1749 "error or a failure" % error_type
1750 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001751
1752 unittest_fn(self, test, err)
1753 if self.current_test_case_info:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001754 self.result_string = "%s [ temp dir used by test case: %s ]" % (
1755 error_type_str,
1756 self.current_test_case_info.tempdir,
1757 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001758 self.symlink_failed()
1759 self.failed_test_cases_info.add(self.current_test_case_info)
1760 if is_core_present(self.current_test_case_info.tempdir):
1761 if not self.current_test_case_info.core_crash_test:
1762 if isinstance(test, unittest.suite._ErrorHolder):
1763 test_name = str(test)
1764 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001765 test_name = "'{!s}' ({!s})".format(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001766 get_testcase_doc_name(test), test.id()
1767 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001768 self.current_test_case_info.core_crash_test = test_name
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001769 self.core_crash_test_cases_info.add(self.current_test_case_info)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001770 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001771 self.result_string = "%s [no temp dir]" % error_type_str
juraj.linkes40dd73b2018-09-21 13:55:16 +02001772
1773 self.send_result_through_pipe(test, error_type)
1774
Damjan Marionf56b77a2016-10-03 19:44:57 +02001775 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001776 """
1777 Record a test failed result
1778
1779 :param test:
1780 :param err: error message
1781
1782 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001783 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001784
Damjan Marionf56b77a2016-10-03 19:44:57 +02001785 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001786 """
1787 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001788
Klement Sekeraf62ae122016-10-11 11:47:09 +02001789 :param test:
1790 :param err: error message
1791
1792 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001793 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001794
Damjan Marionf56b77a2016-10-03 19:44:57 +02001795 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001796 """
1797 Get test description
1798
1799 :param test:
1800 :returns: test description
1801
1802 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001803 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001804
Damjan Marionf56b77a2016-10-03 19:44:57 +02001805 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001806 """
1807 Start a test
1808
1809 :param test:
1810
1811 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001812
1813 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001814 if test.__class__ in self.printed:
1815 return
1816
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001817 test_doc = getdoc(test)
1818 if not test_doc:
1819 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001820
Klement Sekeraea6236b2021-03-25 14:03:44 +01001821 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001822 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001823 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001824 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001825
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001826 # This block may overwrite the colorized title above,
1827 # but we want this to stand out and be fixed
1828 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001829 test_title = colorize(f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001830
Naveen Joy6eaeea92021-09-09 17:57:02 -07001831 if test.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001832 test_title = colorize(f"FIXME with ASAN: {test_title}", RED)
Naveen Joy6eaeea92021-09-09 17:57:02 -07001833 test.skip_fixme_asan()
1834
Dave Wallacee95b2462022-09-18 22:28:44 -04001835 if is_distro_ubuntu2204 == True and test.has_tag(
1836 TestCaseTag.FIXME_UBUNTU2204
1837 ):
1838 test_title = colorize(f"FIXME on Ubuntu-22.04: {test_title}", RED)
1839 test.skip_fixme_ubuntu2204()
1840
Dave Wallace670724c2022-09-20 21:52:18 -04001841 if is_distro_debian11 == True and test.has_tag(TestCaseTag.FIXME_DEBIAN11):
1842 test_title = colorize(f"FIXME on Debian-11: {test_title}", RED)
1843 test.skip_fixme_debian11()
1844
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001845 if hasattr(test, "vpp_worker_count"):
Klement Sekeraea6236b2021-03-25 14:03:44 +01001846 if test.vpp_worker_count == 0:
1847 test_title += " [main thread only]"
1848 elif test.vpp_worker_count == 1:
1849 test_title += " [1 worker thread]"
1850 else:
1851 test_title += f" [{test.vpp_worker_count} worker threads]"
1852
Klement Sekera558ceab2021-04-08 19:37:41 +02001853 if test.__class__.skipped_due_to_cpu_lack:
1854 test_title = colorize(
1855 f"{test_title} [skipped - not enough cpus, "
1856 f"required={test.__class__.get_cpus_required()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001857 f"available={max_vpp_cpus}]",
1858 YELLOW,
1859 )
Klement Sekera558ceab2021-04-08 19:37:41 +02001860
1861 print(double_line_delim)
1862 print(test_title)
1863 print(double_line_delim)
1864 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001865
1866 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001867 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001868 unittest.TestResult.startTest(self, test)
1869 if self.verbosity > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001870 self.stream.writeln("Starting " + self.getDescription(test) + " ...")
Klement Sekeraf62ae122016-10-11 11:47:09 +02001871 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001872
Damjan Marionf56b77a2016-10-03 19:44:57 +02001873 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001874 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001875 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001876
1877 :param test:
1878
1879 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001880 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001881
Damjan Marionf56b77a2016-10-03 19:44:57 +02001882 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001883 self.stream.writeln(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001884 self.stream.writeln(
1885 "%-73s%s" % (self.getDescription(test), self.result_string)
1886 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001887 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001888 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001889 self.stream.writeln(
1890 "%-68s %4.2f %s"
1891 % (
1892 self.getDescription(test),
1893 time.time() - self.start_test,
1894 self.result_string,
1895 )
1896 )
juraj.linkescae64f82018-09-19 15:01:47 +02001897
1898 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001899
Damjan Marionf56b77a2016-10-03 19:44:57 +02001900 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001901 """
1902 Print errors from running the test case
1903 """
juraj.linkesabec0122018-11-16 17:28:56 +01001904 if len(self.errors) > 0 or len(self.failures) > 0:
1905 self.stream.writeln()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001906 self.printErrorList("ERROR", self.errors)
1907 self.printErrorList("FAIL", self.failures)
juraj.linkesabec0122018-11-16 17:28:56 +01001908
1909 # ^^ that is the last output from unittest before summary
1910 if not self.runner.print_summary:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001911 devnull = unittest.runner._WritelnDecorator(open(os.devnull, "w"))
juraj.linkesabec0122018-11-16 17:28:56 +01001912 self.stream = devnull
1913 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001914
Damjan Marionf56b77a2016-10-03 19:44:57 +02001915 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001916 """
1917 Print error list to the output stream together with error type
1918 and test case description.
1919
1920 :param flavour: error type
1921 :param errors: iterable errors
1922
1923 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001924 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001925 self.stream.writeln(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001926 self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001927 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001928 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001929
1930
Damjan Marionf56b77a2016-10-03 19:44:57 +02001931class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001932 """
Klement Sekera104543f2017-02-03 07:29:43 +01001933 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001934 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001935
Klement Sekeraf62ae122016-10-11 11:47:09 +02001936 @property
1937 def resultclass(self):
1938 """Class maintaining the results of the tests"""
1939 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001940
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001941 def __init__(
1942 self,
1943 keep_alive_pipe=None,
1944 descriptions=True,
1945 verbosity=1,
1946 result_pipe=None,
1947 failfast=False,
1948 buffer=False,
1949 resultclass=None,
1950 print_summary=True,
1951 **kwargs,
1952 ):
Klement Sekera7a161da2017-01-17 13:42:48 +01001953 # ignore stream setting here, use hard-coded stdout to be in sync
1954 # with prints from VppTestCase methods ...
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001955 super(VppTestRunner, self).__init__(
1956 sys.stdout, descriptions, verbosity, failfast, buffer, resultclass, **kwargs
1957 )
juraj.linkesccfead62018-11-21 13:20:43 +01001958 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001959
juraj.linkesabec0122018-11-16 17:28:56 +01001960 self.orig_stream = self.stream
1961 self.resultclass.test_framework_result_pipe = result_pipe
1962
1963 self.print_summary = print_summary
1964
1965 def _makeResult(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001966 return self.resultclass(self.stream, self.descriptions, self.verbosity, self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001967
Damjan Marionf56b77a2016-10-03 19:44:57 +02001968 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001969 """
1970 Run the tests
1971
1972 :param test:
1973
1974 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001975 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001976
1977 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001978 if not self.print_summary:
1979 self.stream = self.orig_stream
1980 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001981 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001982
1983
1984class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001985 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1986 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001987 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001988 self.args = executable_args
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001989 if hasattr(self, "testcase") and self.testcase.debug_all:
Dave Wallace24564332019-10-21 02:53:14 +00001990 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001991 self.args = [
1992 "/usr/bin/gdbserver",
1993 "localhost:{port}".format(port=self.testcase.gdbserver_port),
1994 ] + args
1995 elif self.testcase.debug_gdb and hasattr(self, "wait_for_gdb"):
Dave Wallace24564332019-10-21 02:53:14 +00001996 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001997 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001998 self.app_name = os.path.basename(self.app_bin)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001999 if hasattr(self, "role"):
2000 self.app_name += " {role}".format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002001 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07002002 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002003 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05002004 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07002005
Dave Wallace24564332019-10-21 02:53:14 +00002006 def wait_for_enter(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002007 if not hasattr(self, "testcase"):
Dave Wallace24564332019-10-21 02:53:14 +00002008 return
2009 if self.testcase.debug_all and self.testcase.debug_gdbserver:
2010 print()
2011 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002012 print(
2013 "Spawned GDB Server for '{app}' with PID: {pid}".format(
2014 app=self.app_name, pid=self.process.pid
2015 )
2016 )
Dave Wallace24564332019-10-21 02:53:14 +00002017 elif self.testcase.debug_all and self.testcase.debug_gdb:
2018 print()
2019 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002020 print(
2021 "Spawned '{app}' with PID: {pid}".format(
2022 app=self.app_name, pid=self.process.pid
2023 )
2024 )
Dave Wallace24564332019-10-21 02:53:14 +00002025 else:
2026 return
2027 print(single_line_delim)
2028 print("You can debug '{app}' using:".format(app=self.app_name))
2029 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002030 print(
2031 "sudo gdb "
2032 + self.app_bin
2033 + " -ex 'target remote localhost:{port}'".format(
2034 port=self.testcase.gdbserver_port
2035 )
2036 )
2037 print(
2038 "Now is the time to attach gdb by running the above "
2039 "command, set up breakpoints etc., then resume from "
2040 "within gdb by issuing the 'continue' command"
2041 )
Dave Wallace24564332019-10-21 02:53:14 +00002042 self.testcase.gdbserver_port += 1
2043 elif self.testcase.debug_gdb:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002044 print(
2045 "sudo gdb "
2046 + self.app_bin
2047 + " -ex 'attach {pid}'".format(pid=self.process.pid)
2048 )
2049 print(
2050 "Now is the time to attach gdb by running the above "
2051 "command and set up breakpoints etc., then resume from"
2052 " within gdb by issuing the 'continue' command"
2053 )
Dave Wallace24564332019-10-21 02:53:14 +00002054 print(single_line_delim)
2055 input("Press ENTER to continue running the testcase...")
2056
Neale Ranns812ed392017-10-16 04:20:13 -07002057 def run(self):
2058 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002059 if not os.path.exists(executable) or not os.access(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002060 executable, os.F_OK | os.X_OK
2061 ):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002062 # Exit code that means some system file did not exist,
2063 # could not be opened, or had some other kind of error.
2064 self.result = os.EX_OSFILE
2065 raise EnvironmentError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002066 "executable '%s' is not found or executable." % executable
2067 )
2068 self.logger.debug(
2069 "Running executable '{app}': '{cmd}'".format(
2070 app=self.app_name, cmd=" ".join(self.args)
2071 )
2072 )
Neale Ranns812ed392017-10-16 04:20:13 -07002073 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05002074 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07002075 env["CK_LOG_FILE_NAME"] = "-"
2076 self.process = subprocess.Popen(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002077 ["stdbuf", "-o0", "-e0"] + self.args,
2078 shell=False,
2079 env=env,
2080 preexec_fn=os.setpgrp,
2081 stdout=subprocess.PIPE,
2082 stderr=subprocess.PIPE,
2083 )
Dave Wallace24564332019-10-21 02:53:14 +00002084 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07002085 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00002086 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07002087 self.logger.info("Return code is `%s'" % self.process.returncode)
2088 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002089 self.logger.info(
2090 "Executable `{app}' wrote to stdout:".format(app=self.app_name)
2091 )
Neale Ranns812ed392017-10-16 04:20:13 -07002092 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002093 self.logger.info(out.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002094 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002095 self.logger.info(
2096 "Executable `{app}' wrote to stderr:".format(app=self.app_name)
2097 )
Neale Ranns812ed392017-10-16 04:20:13 -07002098 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002099 self.logger.info(err.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002100 self.logger.info(single_line_delim)
2101 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002102
Klement Sekera6aa58b72019-05-16 14:34:55 +02002103
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002104if __name__ == "__main__":
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002105 pass