blob: f39794f22cf23af3b6bb7745ace20f153d7f0ed3 [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
Dave Wallace8a0a9d22022-10-04 22:02:49 -0400277 # marks suites broken on debug vpp image
278 FIXME_VPP_DEBUG = 6
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000279
280
281def create_tag_decorator(e):
282 def decorator(cls):
283 try:
284 cls.test_tags.append(e)
285 except AttributeError:
286 cls.test_tags = [e]
287 return cls
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200288
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000289 return decorator
290
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000291
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000292tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000293tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Naveen Joy6eaeea92021-09-09 17:57:02 -0700294tag_fixme_asan = create_tag_decorator(TestCaseTag.FIXME_ASAN)
Dave Wallacee95b2462022-09-18 22:28:44 -0400295tag_fixme_ubuntu2204 = create_tag_decorator(TestCaseTag.FIXME_UBUNTU2204)
Dave Wallace670724c2022-09-20 21:52:18 -0400296tag_fixme_debian11 = create_tag_decorator(TestCaseTag.FIXME_DEBIAN11)
Dave Wallace8a0a9d22022-10-04 22:02:49 -0400297tag_fixme_vpp_debug = create_tag_decorator(TestCaseTag.FIXME_VPP_DEBUG)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000298
299
Klement Sekerae2636852021-03-16 12:52:12 +0100300class DummyVpp:
301 returncode = None
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200302 pid = 0xCAFEBAFE
Klement Sekerae2636852021-03-16 12:52:12 +0100303
304 def poll(self):
305 pass
306
307 def terminate(self):
308 pass
309
310
Klement Sekera558ceab2021-04-08 19:37:41 +0200311class CPUInterface(ABC):
312 cpus = []
313 skipped_due_to_cpu_lack = False
314
315 @classmethod
316 @abstractmethod
317 def get_cpus_required(cls):
318 pass
319
320 @classmethod
321 def assign_cpus(cls, cpus):
322 cls.cpus = cpus
323
324
Naveen Joyc872cec2022-08-30 13:59:03 -0700325@use_running
Klement Sekera558ceab2021-04-08 19:37:41 +0200326class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100327 """This subclass is a base class for VPP test cases that are implemented as
328 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200329 """
330
Arthur de Kerhordb023802021-03-11 10:26:54 -0800331 extra_vpp_statseg_config = ""
Klement Sekerad3e0d102023-01-26 12:35:35 +0100332 extra_vpp_config = []
Ole Troana45dc072018-12-21 16:04:22 +0100333 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500334 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400335 vapi_response_timeout = 5
Klement Sekera140af152022-02-18 10:30:51 +0000336 remove_configured_vpp_objects_on_tear_down = True
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100337
Klement Sekeraf62ae122016-10-11 11:47:09 +0200338 @property
339 def packet_infos(self):
340 """List of packet infos"""
341 return self._packet_infos
342
Klement Sekeradab231a2016-12-21 08:50:14 +0100343 @classmethod
344 def get_packet_count_for_if_idx(cls, dst_if_index):
345 """Get the number of packet info for specified destination if index"""
346 if dst_if_index in cls._packet_count_for_dst_if_idx:
347 return cls._packet_count_for_dst_if_idx[dst_if_index]
348 else:
349 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200350
351 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000352 def has_tag(cls, tag):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200353 """if the test case has a given tag - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000354 try:
355 return tag in cls.test_tags
356 except AttributeError:
357 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000358 return False
359
360 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000361 def is_tagged_run_solo(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200362 """if the test case class is timing-sensitive - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000363 return cls.has_tag(TestCaseTag.RUN_SOLO)
364
365 @classmethod
Naveen Joy6eaeea92021-09-09 17:57:02 -0700366 def skip_fixme_asan(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200367 """if @tag_fixme_asan & ASan is enabled - mark for skip"""
Naveen Joy6eaeea92021-09-09 17:57:02 -0700368 if cls.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200369 vpp_extra_cmake_args = os.environ.get("VPP_EXTRA_CMAKE_ARGS", "")
370 if "DVPP_ENABLE_SANITIZE_ADDR=ON" in vpp_extra_cmake_args:
Naveen Joy6eaeea92021-09-09 17:57:02 -0700371 cls = unittest.skip("Skipping @tag_fixme_asan tests")(cls)
372
373 @classmethod
Dave Wallacee95b2462022-09-18 22:28:44 -0400374 def skip_fixme_ubuntu2204(cls):
375 """if distro is ubuntu 22.04 and @tag_fixme_ubuntu2204 mark for skip"""
376 if cls.has_tag(TestCaseTag.FIXME_UBUNTU2204):
377 cls = unittest.skip("Skipping @tag_fixme_ubuntu2204 tests")(cls)
378
379 @classmethod
Dave Wallace670724c2022-09-20 21:52:18 -0400380 def skip_fixme_debian11(cls):
381 """if distro is Debian-11 and @tag_fixme_debian11 mark for skip"""
382 if cls.has_tag(TestCaseTag.FIXME_DEBIAN11):
383 cls = unittest.skip("Skipping @tag_fixme_debian11 tests")(cls)
384
385 @classmethod
Dave Wallace8a0a9d22022-10-04 22:02:49 -0400386 def skip_fixme_vpp_debug(cls):
387 cls = unittest.skip("Skipping @tag_fixme_vpp_debug tests")(cls)
388
389 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200390 def instance(cls):
391 """Return the instance of this testcase"""
392 return cls.test_instance
393
Damjan Marionf56b77a2016-10-03 19:44:57 +0200394 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200395 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000396 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200397 cls.debug_core = False
398 cls.debug_gdb = False
399 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000400 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100401 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200402 if d is None:
403 return
404 dl = d.lower()
405 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200406 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000407 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200408 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000409 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200410 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100411 elif dl == "attach":
412 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200413 else:
414 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000415 if dl == "gdb-all" or dl == "gdbserver-all":
416 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200417
Klement Sekera558ceab2021-04-08 19:37:41 +0200418 @classmethod
419 def get_vpp_worker_count(cls):
420 if not hasattr(cls, "vpp_worker_count"):
421 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
422 cls.vpp_worker_count = 0
423 else:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200424 cls.vpp_worker_count = config.vpp_worker_count
Klement Sekera558ceab2021-04-08 19:37:41 +0200425 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200426
Klement Sekera558ceab2021-04-08 19:37:41 +0200427 @classmethod
428 def get_cpus_required(cls):
429 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200430
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800431 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200432 def setUpConstants(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200433 """Set-up the test case class based on environment variables"""
Klement Sekerab23ffd72021-05-31 16:08:53 +0200434 cls.step = config.step
435 cls.plugin_path = ":".join(config.vpp_plugin_dir)
436 cls.test_plugin_path = ":".join(config.vpp_test_plugin_dir)
437 cls.extern_plugin_path = ":".join(config.extern_plugin_dir)
Ole Troana45dc072018-12-21 16:04:22 +0100438 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100439 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100440 debug_cli = "cli-listen localhost:5002"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200441 size = re.search(r"\d+[gG]", config.coredump_size)
442 if size:
443 coredump_size = f"coredump-size {config.coredump_size}".lower()
444 else:
Ole Troana45dc072018-12-21 16:04:22 +0100445 coredump_size = "coredump-size unlimited"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200446 default_variant = config.variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000447 if default_variant is not None:
Benoît Ganne09ef5922022-07-29 10:52:34 +0200448 default_variant = "default { variant %s 100 }" % default_variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000449 else:
450 default_variant = ""
451
Klement Sekerab23ffd72021-05-31 16:08:53 +0200452 api_fuzzing = config.api_fuzz
Dave Barach77841402020-04-29 17:04:10 -0400453 if api_fuzzing is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200454 api_fuzzing = "off"
Dave Barach77841402020-04-29 17:04:10 -0400455
Klement Sekera8d815022021-03-15 16:58:10 +0100456 cls.vpp_cmdline = [
Klement Sekerab23ffd72021-05-31 16:08:53 +0200457 config.vpp,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200458 "unix",
459 "{",
460 "nodaemon",
461 debug_cli,
462 "full-coredump",
463 coredump_size,
464 "runtime-dir",
465 cls.tempdir,
Klement Sekera8d815022021-03-15 16:58:10 +0100466 "}",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200467 "api-trace",
468 "{",
469 "on",
470 "}",
471 "api-segment",
472 "{",
473 "prefix",
474 cls.get_api_segment_prefix(),
475 "}",
476 "cpu",
477 "{",
478 "main-core",
479 str(cls.cpus[0]),
480 ]
481 if cls.extern_plugin_path not in (None, ""):
482 cls.extra_vpp_plugin_config.append("add-path %s" % cls.extern_plugin_path)
483 if cls.get_vpp_worker_count():
484 cls.vpp_cmdline.extend(
485 ["corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])]
486 )
487 cls.vpp_cmdline.extend(
488 [
489 "}",
490 "physmem",
491 "{",
492 "max-size",
493 "32m",
494 "}",
495 "statseg",
496 "{",
497 "socket-name",
498 cls.get_stats_sock_path(),
499 cls.extra_vpp_statseg_config,
500 "}",
501 "socksvr",
502 "{",
503 "socket-name",
504 cls.get_api_sock_path(),
505 "}",
506 "node { ",
507 default_variant,
508 "}",
509 "api-fuzz {",
510 api_fuzzing,
511 "}",
512 "plugins",
513 "{",
514 "plugin",
515 "dpdk_plugin.so",
516 "{",
517 "disable",
518 "}",
519 "plugin",
520 "rdma_plugin.so",
521 "{",
522 "disable",
523 "}",
524 "plugin",
525 "lisp_unittest_plugin.so",
526 "{",
527 "enable",
528 "}",
529 "plugin",
530 "unittest_plugin.so",
531 "{",
532 "enable",
533 "}",
534 ]
535 + cls.extra_vpp_plugin_config
536 + [
537 "}",
538 ]
539 )
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000540
Klement Sekerad3e0d102023-01-26 12:35:35 +0100541 if cls.extra_vpp_config is not None:
542 cls.vpp_cmdline.extend(cls.extra_vpp_config)
Dave Barach7d31ab22019-05-08 19:18:18 -0400543
Klement Sekerae2636852021-03-16 12:52:12 +0100544 if not cls.debug_attach:
545 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
546 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200547
548 @classmethod
549 def wait_for_enter(cls):
550 if cls.debug_gdbserver:
551 print(double_line_delim)
552 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
553 elif cls.debug_gdb:
554 print(double_line_delim)
555 print("Spawned VPP with PID: %d" % cls.vpp.pid)
556 else:
557 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
558 return
559 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000560 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200561 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200562 print(
563 f"sudo gdb {config.vpp} "
564 f"-ex 'target remote localhost:{cls.gdbserver_port}'"
565 )
566 print(
567 "Now is the time to attach gdb by running the above "
568 "command, set up breakpoints etc., then resume VPP from "
569 "within gdb by issuing the 'continue' command"
570 )
Dave Wallace24564332019-10-21 02:53:14 +0000571 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200572 elif cls.debug_gdb:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200573 print(f"sudo gdb {config.vpp} -ex 'attach {cls.vpp.pid}'")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200574 print(
575 "Now is the time to attach gdb by running the above "
576 "command and set up breakpoints etc., then resume VPP from"
577 " within gdb by issuing the 'continue' command"
578 )
Klement Sekera277b89c2016-10-28 13:20:27 +0200579 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800580 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200581
582 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100583 def attach_vpp(cls):
584 cls.vpp = DummyVpp()
585
586 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200587 def run_vpp(cls):
Dave Wallace670724c2022-09-20 21:52:18 -0400588 if (
589 is_distro_ubuntu2204 == True and cls.has_tag(TestCaseTag.FIXME_UBUNTU2204)
590 ) or (is_distro_debian11 == True and cls.has_tag(TestCaseTag.FIXME_DEBIAN11)):
591 return
Klement Sekera558ceab2021-04-08 19:37:41 +0200592 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200593 cmdline = cls.vpp_cmdline
594
595 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200596 gdbserver = "/usr/bin/gdbserver"
597 if not os.path.isfile(gdbserver) or not os.access(gdbserver, os.X_OK):
598 raise Exception(
599 "gdbserver binary '%s' does not exist or is "
600 "not executable" % gdbserver
601 )
Klement Sekera931be3a2016-11-03 05:36:01 +0100602
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200603 cmdline = [
604 gdbserver,
605 "localhost:{port}".format(port=cls.gdbserver_port),
606 ] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200607 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
608
Klement Sekera931be3a2016-11-03 05:36:01 +0100609 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200610 cls.vpp = subprocess.Popen(
611 cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE
612 )
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800613 except subprocess.CalledProcessError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200614 cls.logger.critical(
615 "Subprocess returned with non-0 return code: (%s)", e.returncode
616 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800617 raise
618 except OSError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200619 cls.logger.critical(
620 "Subprocess returned with OS error: (%s) %s", e.errno, e.strerror
621 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800622 raise
623 except Exception as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200624 cls.logger.exception("Subprocess returned unexpected from %s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100625 raise
626
Klement Sekera277b89c2016-10-28 13:20:27 +0200627 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100628
Damjan Marionf56b77a2016-10-03 19:44:57 +0200629 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000630 def wait_for_coredump(cls):
631 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400632 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000633 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400634 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000635 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400636 ok = False
637 while time.time() < deadline:
638 cls.sleep(1)
639 size = curr_size
640 curr_size = os.path.getsize(corefile)
641 if size == curr_size:
642 ok = True
643 break
644 if not ok:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200645 cls.logger.error(
646 "Timed out waiting for coredump to complete: %s", corefile
647 )
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400648 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200649 cls.logger.error("Coredump complete: %s, size %d", corefile, curr_size)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400650
651 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100652 def get_stats_sock_path(cls):
653 return "%s/stats.sock" % cls.tempdir
654
655 @classmethod
656 def get_api_sock_path(cls):
657 return "%s/api.sock" % cls.tempdir
658
659 @classmethod
660 def get_api_segment_prefix(cls):
661 return os.path.basename(cls.tempdir) # Only used for VAPI
662
663 @classmethod
664 def get_tempdir(cls):
Klement Sekerab3fc6582022-03-10 11:47:45 +0100665 if cls.debug_attach:
666 tmpdir = f"{config.tmp_dir}/unittest-attach-gdb"
667 else:
668 tmpdir = f"{config.tmp_dir}/vpp-unittest-{cls.__name__}"
669 if config.wipe_tmp_dir:
670 shutil.rmtree(tmpdir, ignore_errors=True)
671 os.mkdir(tmpdir)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200672 return tmpdir
673
674 @classmethod
675 def create_file_handler(cls):
676 if config.log_dir is None:
677 cls.file_handler = FileHandler(f"{cls.tempdir}/log.txt")
678 return
679
680 logdir = f"{config.log_dir}/vpp-unittest-{cls.__name__}"
681 if config.wipe_tmp_dir:
682 shutil.rmtree(logdir, ignore_errors=True)
683 os.mkdir(logdir)
684 cls.file_handler = FileHandler(f"{logdir}/log.txt")
Klement Sekerae2636852021-03-16 12:52:12 +0100685
686 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200687 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200688 """
689 Perform class setup before running the testcase
690 Remove shared memory files, start vpp and connect the vpp-api
691 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800692 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100693 cls.logger = get_logger(cls.__name__)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200694 random.seed(config.rnd_seed)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200695 if hasattr(cls, "parallel_handler"):
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100696 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100697 cls.logger.propagate = False
Klement Sekerab23ffd72021-05-31 16:08:53 +0200698 cls.set_debug_flags(config.debug)
Klement Sekerae2636852021-03-16 12:52:12 +0100699 cls.tempdir = cls.get_tempdir()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200700 cls.create_file_handler()
Klement Sekera027dbd52017-04-11 06:01:53 +0200701 cls.file_handler.setFormatter(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200702 Formatter(fmt="%(asctime)s,%(msecs)03d %(message)s", datefmt="%H:%M:%S")
703 )
Klement Sekera027dbd52017-04-11 06:01:53 +0200704 cls.file_handler.setLevel(DEBUG)
705 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100706 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200707 os.chdir(cls.tempdir)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200708 cls.logger.info(
709 "Temporary dir is %s, api socket is %s",
710 cls.tempdir,
711 cls.get_api_sock_path(),
712 )
Klement Sekerab23ffd72021-05-31 16:08:53 +0200713 cls.logger.debug("Random seed is %s", config.rnd_seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200714 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100715 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200716 cls._pcaps = []
717 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200718 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100719 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100720 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200721 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200722 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200723 # need to catch exceptions here because if we raise, then the cleanup
724 # doesn't get called and we might end with a zombie vpp
725 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100726 if cls.debug_attach:
727 cls.attach_vpp()
728 else:
729 cls.run_vpp()
Dave Wallace670724c2022-09-20 21:52:18 -0400730 if not hasattr(cls, "vpp"):
731 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200732 cls.reporter.send_keep_alive(cls, "setUpClass")
juraj.linkes40dd73b2018-09-21 13:55:16 +0200733 VppTestResult.current_test_case_info = TestCaseInfo(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200734 cls.logger, cls.tempdir, cls.vpp.pid, config.vpp
735 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100736 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100737 cls.vpp_stderr_deque = deque()
Naveen Joyc872cec2022-08-30 13:59:03 -0700738 # Pump thread in a non-debug-attached & not running-vpp
739 if not cls.debug_attach and not hasattr(cls, "running_vpp"):
Klement Sekerae2636852021-03-16 12:52:12 +0100740 cls.pump_thread_stop_flag = Event()
741 cls.pump_thread_wakeup_pipe = os.pipe()
742 cls.pump_thread = Thread(target=pump_output, args=(cls,))
743 cls.pump_thread.daemon = True
744 cls.pump_thread.start()
745 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400746 cls.vapi_response_timeout = 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200747 cls.vapi = VppPapiProvider(cls.__name__, cls, cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100748 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400749 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100750 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400751 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100752 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100753 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200754 try:
755 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100756 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200757 cls.vpp_startup_failed = True
758 cls.logger.critical(
759 "VPP died shortly after startup, check the"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200760 " output to standard error for possible cause"
761 )
Klement Sekera3747c752017-04-10 06:30:17 +0200762 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100763 try:
764 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100765 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500766 cls.logger.debug("Exception connecting to vapi: %s" % e)
767 cls.vapi.disconnect()
768
Klement Sekera085f5c02016-11-24 01:59:16 +0100769 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200770 print(
771 colorize(
772 "You're running VPP inside gdbserver but "
773 "VPP-API connection failed, did you forget "
774 "to 'continue' VPP from within gdb?",
775 RED,
776 )
777 )
Ole Troan4376ab22021-03-03 10:40:05 +0100778 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100779 if cls.debug_attach:
780 last_line = cls.vapi.cli("show thread").split("\n")[-2]
781 cls.vpp_worker_count = int(last_line.split(" ")[0])
782 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400783 except vpp_papi.VPPRuntimeError as e:
784 cls.logger.debug("%s" % e)
785 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100786 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000787 except Exception as e:
788 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400789 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100790 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200791
Damjan Marionf56b77a2016-10-03 19:44:57 +0200792 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500793 def _debug_quit(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200794 if cls.debug_gdbserver or cls.debug_gdb:
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500795 try:
796 cls.vpp.poll()
797
798 if cls.vpp.returncode is None:
799 print()
800 print(double_line_delim)
801 print("VPP or GDB server is still running")
802 print(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200803 input(
804 "When done debugging, press ENTER to kill the "
805 "process and finish running the testcase..."
806 )
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500807 except AttributeError:
808 pass
809
810 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200811 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200812 """
813 Disconnect vpp-api, kill vpp and cleanup shared memory files
814 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500815 cls._debug_quit()
Naveen Joyc872cec2022-08-30 13:59:03 -0700816 if hasattr(cls, "running_vpp"):
817 cls.vpp.quit_vpp()
Klement Sekera277b89c2016-10-28 13:20:27 +0200818
juraj.linkes184870a2018-07-16 14:22:01 +0200819 # first signal that we want to stop the pump thread, then wake it up
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200820 if hasattr(cls, "pump_thread_stop_flag"):
juraj.linkes184870a2018-07-16 14:22:01 +0200821 cls.pump_thread_stop_flag.set()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200822 if hasattr(cls, "pump_thread_wakeup_pipe"):
823 os.write(cls.pump_thread_wakeup_pipe[1], b"ding dong wake up")
824 if hasattr(cls, "pump_thread"):
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100825 cls.logger.debug("Waiting for pump thread to stop")
826 cls.pump_thread.join()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200827 if hasattr(cls, "vpp_stderr_reader_thread"):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500828 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100829 cls.vpp_stderr_reader_thread.join()
830
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200831 if hasattr(cls, "vpp"):
832 if hasattr(cls, "vapi"):
Ole Troanfd574082019-11-27 23:12:48 +0100833 cls.logger.debug(cls.vapi.vpp.get_stats())
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200834 cls.logger.debug("Disconnecting class vapi client on %s", cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100835 cls.vapi.disconnect()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200836 cls.logger.debug("Deleting class vapi attribute on %s", cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100837 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200838 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100839 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000840 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100841 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400842 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100843 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100844 try:
845 outs, errs = cls.vpp.communicate(timeout=5)
846 except subprocess.TimeoutExpired:
847 cls.vpp.kill()
848 outs, errs = cls.vpp.communicate()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200849 cls.logger.debug("Deleting class vpp attribute on %s", cls.__name__)
Naveen Joyc872cec2022-08-30 13:59:03 -0700850 if not cls.debug_attach and not hasattr(cls, "running_vpp"):
Klement Sekerae2636852021-03-16 12:52:12 +0100851 cls.vpp.stdout.close()
852 cls.vpp.stderr.close()
Naveen Joyc872cec2022-08-30 13:59:03 -0700853 # If vpp is a dynamic attribute set by the func use_running,
854 # deletion will result in an AttributeError that we can
855 # safetly pass.
856 try:
857 del cls.vpp
858 except AttributeError:
859 pass
Damjan Marionf56b77a2016-10-03 19:44:57 +0200860
Klement Sekera3747c752017-04-10 06:30:17 +0200861 if cls.vpp_startup_failed:
862 stdout_log = cls.logger.info
863 stderr_log = cls.logger.critical
864 else:
865 stdout_log = cls.logger.info
866 stderr_log = cls.logger.info
867
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200868 if hasattr(cls, "vpp_stdout_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200869 stdout_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200870 stdout_log("VPP output to stdout while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200871 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100872 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200873 with open(cls.tempdir + "/vpp_stdout.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200874 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200875 stdout_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200876 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200877
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200878 if hasattr(cls, "vpp_stderr_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200879 stderr_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200880 stderr_log("VPP output to stderr while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200881 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100882 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200883 with open(cls.tempdir + "/vpp_stderr.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200884 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200885 stderr_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200886 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200887
Damjan Marionf56b77a2016-10-03 19:44:57 +0200888 @classmethod
889 def tearDownClass(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200890 """Perform final cleanup after running all tests in this test-case"""
891 cls.logger.debug("--- tearDownClass() for %s called ---" % cls.__name__)
Dave Wallace670724c2022-09-20 21:52:18 -0400892 if not hasattr(cls, "vpp"):
893 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200894 cls.reporter.send_keep_alive(cls, "tearDownClass")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200895 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200896 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100897 cls.reset_packet_infos()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200898 if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +0100899 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200900
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700901 def show_commands_at_teardown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200902 """Allow subclass specific teardown logging additions."""
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700903 self.logger.info("--- No test specific show commands provided. ---")
904
Damjan Marionf56b77a2016-10-03 19:44:57 +0200905 def tearDown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200906 """Show various debug prints after each test"""
907 self.logger.debug(
908 "--- tearDown() for %s.%s(%s) called ---"
909 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
910 )
Dave Wallace670724c2022-09-20 21:52:18 -0400911 if not hasattr(self, "vpp"):
912 return
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700913
914 try:
915 if not self.vpp_dead:
916 self.logger.debug(self.vapi.cli("show trace max 1000"))
917 self.logger.info(self.vapi.ppcli("show interface"))
918 self.logger.info(self.vapi.ppcli("show hardware"))
919 self.logger.info(self.statistics.set_errors_str())
920 self.logger.info(self.vapi.ppcli("show run"))
921 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400922 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700923 self.logger.info("Logging testcase specific show commands.")
924 self.show_commands_at_teardown()
Klement Sekera140af152022-02-18 10:30:51 +0000925 if self.remove_configured_vpp_objects_on_tear_down:
926 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500927 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000928 m = self._testMethodName
929 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500930 tmp_api_trace = "/tmp/%s" % api_trace
931 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
932 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200933 self.logger.info("Moving %s to %s\n" % (tmp_api_trace, vpp_api_trace_log))
Dmitry Valter71d02aa2023-01-27 12:49:55 +0000934 shutil.move(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100935 except VppTransportSocketIOError:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200936 self.logger.debug(
937 "VppTransportSocketIOError: Vpp dead. Cannot log show commands."
938 )
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700939 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100940 else:
941 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200942
Damjan Marionf56b77a2016-10-03 19:44:57 +0200943 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200944 """Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800945 super(VppTestCase, self).setUp()
Dave Wallace670724c2022-09-20 21:52:18 -0400946 if not hasattr(self, "vpp"):
947 return
Klement Sekera909a6a12017-08-08 04:33:53 +0200948 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100949 if self.vpp_dead:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200950 raise VppDiedError(
951 rv=None,
952 testcase=self.__class__.__name__,
953 method_name=self._testMethodName,
954 )
955 self.sleep(0.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100956 self.vpp_stdout_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200957 "--- test setUp() for %s.%s(%s) starts here ---\n"
958 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
959 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100960 self.vpp_stderr_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200961 "--- test setUp() for %s.%s(%s) starts here ---\n"
962 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
963 )
Klement Sekeraf62ae122016-10-11 11:47:09 +0200964 self.vapi.cli("clear trace")
965 # store the test instance inside the test class - so that objects
966 # holding the class can access instance methods (like assertEqual)
967 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200968
Damjan Marionf56b77a2016-10-03 19:44:57 +0200969 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200970 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200971 """
972 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200973
Klement Sekera75e7d132017-09-20 08:26:30 +0200974 :param interfaces: iterable interface indexes (if None,
975 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200976
Klement Sekeraf62ae122016-10-11 11:47:09 +0200977 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200978 if interfaces is None:
979 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200980 for i in interfaces:
981 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200982
Damjan Marionf56b77a2016-10-03 19:44:57 +0200983 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200984 def register_pcap(cls, intf, worker):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200985 """Register a pcap in the testclass"""
Klement Sekera9225dee2016-12-12 08:36:58 +0100986 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200987 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100988
989 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000990 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400991 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
992 # returns float("2.190522")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200993 timestr = cls.vapi.cli("show clock")
994 head, sep, tail = timestr.partition(",")
995 head, sep, tail = head.partition("Time now")
Dave Barach19718002020-03-11 10:31:36 -0400996 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000997
998 @classmethod
999 def sleep_on_vpp_time(cls, sec):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001000 """Sleep according to time in VPP world"""
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +00001001 # On a busy system with many processes
1002 # we might end up with VPP time being slower than real world
1003 # So take that into account when waiting for VPP to do something
1004 start_time = cls.get_vpp_time()
1005 while cls.get_vpp_time() - start_time < sec:
1006 cls.sleep(0.1)
1007
1008 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +01001009 def pg_start(cls, trace=True):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001010 """Enable the PG, wait till it is done, then clean up"""
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001011 for (intf, worker) in cls._old_pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001012 intf.handle_old_pcap_file(intf.get_in_path(worker), intf.in_history_counter)
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001013 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +01001014 if trace:
1015 cls.vapi.cli("clear trace")
1016 cls.vapi.cli("trace add pg-input 1000")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001017 cls.vapi.cli("packet-generator enable")
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +00001018 # PG, when starts, runs to completion -
1019 # so let's avoid a race condition,
1020 # and wait a little till it's done.
1021 # Then clean it up - and then be gone.
1022 deadline = time.time() + 300
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001023 while cls.vapi.cli("show packet-generator").find("Yes") != -1:
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +00001024 cls.sleep(0.01) # yield
1025 if time.time() > deadline:
1026 cls.logger.error("Timeout waiting for pg to stop")
1027 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001028 for intf, worker in cls._pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001029 cls.vapi.cli("packet-generator delete %s" % intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +02001030 cls._old_pcaps = cls._pcaps
1031 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001032
Damjan Marionf56b77a2016-10-03 19:44:57 +02001033 @classmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001034 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0, mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001035 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001036 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +02001037
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001038 :param interfaces: iterable indexes of the interfaces.
1039 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +02001040
Klement Sekeraf62ae122016-10-11 11:47:09 +02001041 """
1042 result = []
1043 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +00001044 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001045 setattr(cls, intf.name, intf)
1046 result.append(intf)
1047 cls.pg_interfaces = result
1048 return result
1049
Matej Klotton0178d522016-11-04 11:11:44 +01001050 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +00001051 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001052 if not hasattr(cls, "vpp"):
1053 cls.pg_interfaces = []
1054 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001055 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001056 return cls.create_pg_interfaces_internal(
1057 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP4
1058 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001059
1060 @classmethod
1061 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001062 if not hasattr(cls, "vpp"):
1063 cls.pg_interfaces = []
1064 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001065 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001066 return cls.create_pg_interfaces_internal(
1067 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP6
1068 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001069
1070 @classmethod
1071 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001072 if not hasattr(cls, "vpp"):
1073 cls.pg_interfaces = []
1074 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001075 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001076 return cls.create_pg_interfaces_internal(
1077 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1078 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001079
1080 @classmethod
1081 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
Dave Wallace670724c2022-09-20 21:52:18 -04001082 if not hasattr(cls, "vpp"):
1083 cls.pg_interfaces = []
1084 return cls.pg_interfaces
Neale Ranns6197cb72021-06-03 14:43:21 +00001085 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001086 return cls.create_pg_interfaces_internal(
1087 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1088 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001089
1090 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +02001091 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +01001092 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001093 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001094
Klement Sekerab9ef2732018-06-24 22:49:33 +02001095 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001096 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001097 """
Dave Wallace670724c2022-09-20 21:52:18 -04001098 if not hasattr(cls, "vpp"):
1099 cls.lo_interfaces = []
1100 return cls.lo_interfaces
Klement Sekerab9ef2732018-06-24 22:49:33 +02001101 result = [VppLoInterface(cls) for i in range(count)]
1102 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +01001103 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +01001104 cls.lo_interfaces = result
1105 return result
1106
Neale Ranns192b13f2019-03-15 02:16:20 -07001107 @classmethod
1108 def create_bvi_interfaces(cls, count):
1109 """
1110 Create BVI interfaces.
1111
1112 :param count: number of interfaces created.
1113 :returns: List of created interfaces.
1114 """
Dave Wallace670724c2022-09-20 21:52:18 -04001115 if not hasattr(cls, "vpp"):
1116 cls.bvi_interfaces = []
1117 return cls.bvi_interfaces
Neale Ranns192b13f2019-03-15 02:16:20 -07001118 result = [VppBviInterface(cls) for i in range(count)]
1119 for intf in result:
1120 setattr(cls, intf.name, intf)
1121 cls.bvi_interfaces = result
1122 return result
1123
Damjan Marionf56b77a2016-10-03 19:44:57 +02001124 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001125 def extend_packet(packet, size, padding=" "):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001126 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001127 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +02001128 NOTE: Currently works only when Raw layer is present.
1129
1130 :param packet: packet
1131 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +02001132 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +02001133
1134 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001135 packet_len = len(packet) + 4
1136 extend = size - packet_len
1137 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +02001138 num = (extend // len(padding)) + 1
1139 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +02001140
Klement Sekeradab231a2016-12-21 08:50:14 +01001141 @classmethod
1142 def reset_packet_infos(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001143 """Reset the list of packet info objects and packet counts to zero"""
Klement Sekeradab231a2016-12-21 08:50:14 +01001144 cls._packet_infos = {}
1145 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +02001146
Klement Sekeradab231a2016-12-21 08:50:14 +01001147 @classmethod
1148 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001149 """
1150 Create packet info object containing the source and destination indexes
1151 and add it to the testcase's packet info list
1152
Klement Sekeradab231a2016-12-21 08:50:14 +01001153 :param VppInterface src_if: source interface
1154 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +02001155
1156 :returns: _PacketInfo object
1157
1158 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001159 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +01001160 info.index = len(cls._packet_infos)
1161 info.src = src_if.sw_if_index
1162 info.dst = dst_if.sw_if_index
1163 if isinstance(dst_if, VppSubInterface):
1164 dst_idx = dst_if.parent.sw_if_index
1165 else:
1166 dst_idx = dst_if.sw_if_index
1167 if dst_idx in cls._packet_count_for_dst_if_idx:
1168 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1169 else:
1170 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1171 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001172 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001173
Damjan Marionf56b77a2016-10-03 19:44:57 +02001174 @staticmethod
1175 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001176 """
1177 Convert _PacketInfo object to packet payload
1178
1179 :param info: _PacketInfo object
1180
1181 :returns: string containing serialized data from packet info
1182 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001183
1184 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001185 return pack("iiiih", info.index, info.src, info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001186
Damjan Marionf56b77a2016-10-03 19:44:57 +02001187 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001188 def payload_to_info(payload, payload_field="load"):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001189 """
1190 Convert packet payload to _PacketInfo object
1191
1192 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001193 :type payload: <class 'scapy.packet.Raw'>
1194 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001195 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001196 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001197 :returns: _PacketInfo object containing de-serialized data from payload
1198
1199 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001200
1201 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1202 payload_b = getattr(payload, payload_field)[:18]
1203
Damjan Marionf56b77a2016-10-03 19:44:57 +02001204 info = _PacketInfo()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001205 info.index, info.src, info.dst, info.ip, info.proto = unpack("iiiih", payload_b)
Ray Kinsellab8165b92021-09-22 11:24:06 +01001206
1207 # some SRv6 TCs depend on get an exception if bad values are detected
1208 if info.index > 0x4000:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001209 raise ValueError("Index value is invalid")
Ray Kinsellab8165b92021-09-22 11:24:06 +01001210
Damjan Marionf56b77a2016-10-03 19:44:57 +02001211 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001212
Damjan Marionf56b77a2016-10-03 19:44:57 +02001213 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001214 """
1215 Iterate over the packet info list stored in the testcase
1216 Start iteration with first element if info is None
1217 Continue based on index in info if info is specified
1218
1219 :param info: info or None
1220 :returns: next info in list or None if no more infos
1221 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001222 if info is None:
1223 next_index = 0
1224 else:
1225 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001226 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001227 return None
1228 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001229 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001230
Klement Sekeraf62ae122016-10-11 11:47:09 +02001231 def get_next_packet_info_for_interface(self, src_index, info):
1232 """
1233 Search the packet info list for the next packet info with same source
1234 interface index
1235
1236 :param src_index: source interface index to search for
1237 :param info: packet info - where to start the search
1238 :returns: packet info or None
1239
1240 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001241 while True:
1242 info = self.get_next_packet_info(info)
1243 if info is None:
1244 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001245 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001246 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001247
Klement Sekeraf62ae122016-10-11 11:47:09 +02001248 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1249 """
1250 Search the packet info list for the next packet info with same source
1251 and destination interface indexes
1252
1253 :param src_index: source interface index to search for
1254 :param dst_index: destination interface index to search for
1255 :param info: packet info - where to start the search
1256 :returns: packet info or None
1257
1258 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001259 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001260 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001261 if info is None:
1262 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001263 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001264 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001265
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001266 def assert_equal(self, real_value, expected_value, name_or_class=None):
1267 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001268 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001269 return
1270 try:
1271 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001272 msg = msg % (
1273 getdoc(name_or_class).strip(),
1274 real_value,
1275 str(name_or_class(real_value)),
1276 expected_value,
1277 str(name_or_class(expected_value)),
1278 )
Klement Sekera13a83ef2018-03-21 12:35:51 +01001279 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001280 msg = "Invalid %s: %s does not match expected value %s" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001281 name_or_class,
1282 real_value,
1283 expected_value,
1284 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001285
1286 self.assertEqual(real_value, expected_value, msg)
1287
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001288 def assert_in_range(self, real_value, expected_min, expected_max, name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001289 if name is None:
1290 msg = None
1291 else:
1292 msg = "Invalid %s: %s out of range <%s,%s>" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001293 name,
1294 real_value,
1295 expected_min,
1296 expected_max,
1297 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001298 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1299
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001300 def assert_packet_checksums_valid(self, packet, ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001301 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001302 udp_layers = ["UDP", "UDPerror"]
1303 checksum_fields = ["cksum", "chksum"]
Klement Sekerad81ae412018-05-16 10:52:54 +02001304 checksums = []
1305 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001306 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001307 while True:
1308 layer = temp.getlayer(counter)
1309 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001310 layer = layer.copy()
1311 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001312 for cf in checksum_fields:
1313 if hasattr(layer, cf):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001314 if (
1315 ignore_zero_udp_checksums
1316 and 0 == getattr(layer, cf)
1317 and layer.name in udp_layers
1318 ):
Klement Sekerad81ae412018-05-16 10:52:54 +02001319 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001320 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001321 checksums.append((counter, cf))
1322 else:
1323 break
1324 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001325 if 0 == len(checksums):
1326 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001327 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekera738cf732022-11-14 11:26:18 +01001328 for layer, cf in reversed(checksums):
Klement Sekera31da2e32018-06-24 22:49:55 +02001329 calc_sum = getattr(temp[layer], cf)
1330 self.assert_equal(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001331 getattr(received[layer], cf),
1332 calc_sum,
1333 "packet checksum on layer #%d: %s" % (layer, temp[layer].name),
1334 )
Klement Sekera31da2e32018-06-24 22:49:55 +02001335 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001336 "Checksum field `%s` on `%s` layer has correct value `%s`"
1337 % (cf, temp[layer].name, calc_sum)
1338 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001339
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001340 def assert_checksum_valid(
Klement Sekera738cf732022-11-14 11:26:18 +01001341 self,
1342 received_packet,
1343 layer,
1344 checksum_field_names=["chksum", "cksum"],
1345 ignore_zero_checksum=False,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001346 ):
1347 """Check checksum of received packet on given layer"""
Klement Sekera738cf732022-11-14 11:26:18 +01001348 layer_copy = received_packet[layer].copy()
1349 layer_copy.remove_payload()
1350 field_name = None
1351 for f in checksum_field_names:
1352 if hasattr(layer_copy, f):
1353 field_name = f
1354 break
1355 if field_name is None:
1356 raise Exception(
1357 f"Layer `{layer}` has none of checksum fields: `{checksum_field_names}`."
1358 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001359 received_packet_checksum = getattr(received_packet[layer], field_name)
1360 if ignore_zero_checksum and 0 == received_packet_checksum:
1361 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001362 recalculated = received_packet.__class__(scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001363 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001364 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001365 self.assert_equal(
1366 received_packet_checksum,
1367 getattr(recalculated[layer], field_name),
Klement Sekera738cf732022-11-14 11:26:18 +01001368 f"packet checksum (field: {field_name}) on layer: %s" % layer,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001369 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001370
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001371 def assert_ip_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1372 self.assert_checksum_valid(
1373 received_packet, "IP", ignore_zero_checksum=ignore_zero_checksum
1374 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001375
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001376 def assert_tcp_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1377 self.assert_checksum_valid(
1378 received_packet, "TCP", ignore_zero_checksum=ignore_zero_checksum
1379 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001380
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001381 def assert_udp_checksum_valid(self, received_packet, ignore_zero_checksum=True):
1382 self.assert_checksum_valid(
1383 received_packet, "UDP", ignore_zero_checksum=ignore_zero_checksum
1384 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001385
1386 def assert_embedded_icmp_checksum_valid(self, received_packet):
1387 if received_packet.haslayer(IPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001388 self.assert_checksum_valid(received_packet, "IPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001389 if received_packet.haslayer(TCPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001390 self.assert_checksum_valid(received_packet, "TCPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001391 if received_packet.haslayer(UDPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001392 self.assert_checksum_valid(
1393 received_packet, "UDPerror", ignore_zero_checksum=True
1394 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001395 if received_packet.haslayer(ICMPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001396 self.assert_checksum_valid(received_packet, "ICMPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001397
1398 def assert_icmp_checksum_valid(self, received_packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001399 self.assert_checksum_valid(received_packet, "ICMP")
Klement Sekerad81ae412018-05-16 10:52:54 +02001400 self.assert_embedded_icmp_checksum_valid(received_packet)
1401
1402 def assert_icmpv6_checksum_valid(self, pkt):
1403 if pkt.haslayer(ICMPv6DestUnreach):
Klement Sekera738cf732022-11-14 11:26:18 +01001404 self.assert_checksum_valid(pkt, "ICMPv6DestUnreach")
Klement Sekerad81ae412018-05-16 10:52:54 +02001405 self.assert_embedded_icmp_checksum_valid(pkt)
1406 if pkt.haslayer(ICMPv6EchoRequest):
Klement Sekera738cf732022-11-14 11:26:18 +01001407 self.assert_checksum_valid(pkt, "ICMPv6EchoRequest")
Klement Sekerad81ae412018-05-16 10:52:54 +02001408 if pkt.haslayer(ICMPv6EchoReply):
Klement Sekera738cf732022-11-14 11:26:18 +01001409 self.assert_checksum_valid(pkt, "ICMPv6EchoReply")
Klement Sekerad81ae412018-05-16 10:52:54 +02001410
Klement Sekera107ad732022-02-18 10:32:08 +00001411 def get_counter(self, counter):
Klement Sekera3a343d42019-05-16 14:35:46 +02001412 if counter.startswith("/"):
1413 counter_value = self.statistics.get_counter(counter)
1414 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001415 counters = self.vapi.cli("sh errors").split("\n")
Klement Sekera6aa58b72019-05-16 14:34:55 +02001416 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001417 for i in range(1, len(counters) - 1):
1418 results = counters[i].split()
1419 if results[1] == counter:
1420 counter_value = int(results[0])
1421 break
1422 return counter_value
1423
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001424 def assert_counter_equal(self, counter, expected_value, thread=None, index=0):
Klement Sekera107ad732022-02-18 10:32:08 +00001425 c = self.get_counter(counter)
1426 if thread is not None:
1427 c = c[thread][index]
1428 else:
1429 c = sum(x[index] for x in c)
1430 self.assert_equal(c, expected_value, "counter `%s'" % counter)
1431
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001432 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera107ad732022-02-18 10:32:08 +00001433 counter_value = self.get_counter(counter)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001434 self.assert_equal(
1435 counter_value, expected_value, "packet counter `%s'" % counter
1436 )
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001437
Ole Troan233e4682019-05-16 15:01:34 +02001438 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001439 counter_value = self.statistics[counter].sum()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001440 self.assert_equal(counter_value, expected_value, "error counter `%s'" % counter)
Ole Troan233e4682019-05-16 15:01:34 +02001441
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001442 @classmethod
1443 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001444
1445 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1446 # * by Guido, only the main thread can be interrupted.
1447 # */
1448 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1449 if timeout == 0:
1450 # yield quantum
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001451 if hasattr(os, "sched_yield"):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001452 os.sched_yield()
1453 else:
1454 time.sleep(0)
1455 return
1456
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001457 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001458 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001459 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001460 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001461 if after - before > 2 * timeout:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001462 cls.logger.error(
1463 "unexpected self.sleep() result - slept for %es instead of ~%es!",
1464 after - before,
1465 timeout,
1466 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001467
1468 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001469 "Finished sleep (%s) - slept %es (wanted %es)",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001470 remark,
1471 after - before,
1472 timeout,
1473 )
Klement Sekeraa57a9702017-02-02 06:58:07 +01001474
Benoît Ganne56eccdb2021-08-20 09:18:31 +02001475 def virtual_sleep(self, timeout, remark=None):
1476 self.logger.debug("Moving VPP time by %s (%s)", timeout, remark)
1477 self.vapi.cli("set clock adjust %s" % timeout)
1478
Benoît Ganne8c45e512021-02-19 16:39:13 +01001479 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001480 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001481 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001482 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001483
Klement Sekeraad3187f2022-02-18 10:34:35 +00001484 def snapshot_stats(self, stats_diff):
1485 """Return snapshot of interesting stats based on diff dictionary."""
1486 stats_snapshot = {}
1487 for sw_if_index in stats_diff:
1488 for counter in stats_diff[sw_if_index]:
1489 stats_snapshot[counter] = self.statistics[counter]
1490 self.logger.debug(f"Took statistics stats_snapshot: {stats_snapshot}")
1491 return stats_snapshot
1492
1493 def compare_stats_with_snapshot(self, stats_diff, stats_snapshot):
1494 """Assert appropriate difference between current stats and snapshot."""
1495 for sw_if_index in stats_diff:
1496 for cntr, diff in stats_diff[sw_if_index].items():
1497 if sw_if_index == "err":
1498 self.assert_equal(
1499 self.statistics[cntr].sum(),
1500 stats_snapshot[cntr].sum() + diff,
1501 f"'{cntr}' counter value (previous value: "
1502 f"{stats_snapshot[cntr].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001503 f"expected diff: {diff})",
1504 )
Klement Sekeraad3187f2022-02-18 10:34:35 +00001505 else:
1506 try:
1507 self.assert_equal(
1508 self.statistics[cntr][:, sw_if_index].sum(),
1509 stats_snapshot[cntr][:, sw_if_index].sum() + diff,
1510 f"'{cntr}' counter value (previous value: "
1511 f"{stats_snapshot[cntr][:, sw_if_index].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001512 f"expected diff: {diff})",
1513 )
Klement Sekera01578852023-01-26 13:14:01 +01001514 except IndexError as e:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001515 # if diff is 0, then this most probably a case where
1516 # test declares multiple interfaces but traffic hasn't
1517 # passed through this one yet - which means the counter
1518 # value is 0 and can be ignored
1519 if 0 != diff:
Klement Sekera01578852023-01-26 13:14:01 +01001520 raise Exception(
1521 f"Couldn't sum counter: {cntr} on sw_if_index: {sw_if_index}"
1522 ) from e
Klement Sekeraad3187f2022-02-18 10:34:35 +00001523
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001524 def send_and_assert_no_replies(
1525 self, intf, pkts, remark="", timeout=None, stats_diff=None, trace=True, msg=None
1526 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001527 if stats_diff:
1528 stats_snapshot = self.snapshot_stats(stats_diff)
1529
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001530 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001531
1532 try:
1533 if not timeout:
1534 timeout = 1
1535 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +00001536 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001537 timeout = 0.1
1538 finally:
1539 if trace:
1540 if msg:
1541 self.logger.debug(f"send_and_assert_no_replies: {msg}")
1542 self.logger.debug(self.vapi.cli("show trace"))
1543
1544 if stats_diff:
1545 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -08001546
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001547 def send_and_expect(
1548 self,
1549 intf,
1550 pkts,
1551 output,
1552 n_rx=None,
1553 worker=None,
1554 trace=True,
1555 msg=None,
1556 stats_diff=None,
1557 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001558 if stats_diff:
1559 stats_snapshot = self.snapshot_stats(stats_diff)
1560
Neale Rannsd7603d92019-03-28 08:56:10 +00001561 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +00001562 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001563 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001564 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +02001565 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001566 if msg:
1567 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +02001568 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +00001569
1570 if stats_diff:
1571 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1572
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001573 return rx
1574
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001575 def send_and_expect_load_balancing(
1576 self, input, pkts, outputs, worker=None, trace=True
1577 ):
Neale Ranns699bea22022-02-17 09:22:16 +00001578 self.pg_send(input, pkts, worker=worker, trace=trace)
1579 rxs = []
1580 for oo in outputs:
1581 rx = oo._get_capture(1)
1582 self.assertNotEqual(0, len(rx))
1583 rxs.append(rx)
1584 if trace:
1585 self.logger.debug(self.vapi.cli("show trace"))
1586 return rxs
1587
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001588 def send_and_expect_some(self, intf, pkts, output, worker=None, trace=True):
Neale Ranns5c6dd172022-02-17 09:08:47 +00001589 self.pg_send(intf, pkts, worker=worker, trace=trace)
1590 rx = output._get_capture(1)
1591 if trace:
1592 self.logger.debug(self.vapi.cli("show trace"))
1593 self.assertTrue(len(rx) > 0)
1594 self.assertTrue(len(rx) < len(pkts))
1595 return rx
1596
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001597 def send_and_expect_only(self, intf, pkts, output, timeout=None, stats_diff=None):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001598 if stats_diff:
1599 stats_snapshot = self.snapshot_stats(stats_diff)
1600
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001601 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001602 rx = output.get_capture(len(pkts))
1603 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001604 if not timeout:
1605 timeout = 1
1606 for i in self.pg_interfaces:
1607 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +00001608 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001609 timeout = 0.1
1610
Klement Sekeraad3187f2022-02-18 10:34:35 +00001611 if stats_diff:
1612 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1613
Neale Ranns52fae862018-01-08 04:41:42 -08001614 return rx
1615
Damjan Marionf56b77a2016-10-03 19:44:57 +02001616
juraj.linkes184870a2018-07-16 14:22:01 +02001617def get_testcase_doc_name(test):
1618 return getdoc(test.__class__).splitlines()[0]
1619
1620
Ole Trøan5ba91592018-11-22 10:01:09 +00001621def get_test_description(descriptions, test):
1622 short_description = test.shortDescription()
1623 if descriptions and short_description:
1624 return short_description
1625 else:
1626 return str(test)
1627
1628
juraj.linkes40dd73b2018-09-21 13:55:16 +02001629class TestCaseInfo(object):
1630 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1631 self.logger = logger
1632 self.tempdir = tempdir
1633 self.vpp_pid = vpp_pid
1634 self.vpp_bin_path = vpp_bin_path
1635 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001636
1637
Damjan Marionf56b77a2016-10-03 19:44:57 +02001638class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001639 """
1640 @property result_string
1641 String variable to store the test case result string.
1642 @property errors
1643 List variable containing 2-tuples of TestCase instances and strings
1644 holding formatted tracebacks. Each tuple represents a test which
1645 raised an unexpected exception.
1646 @property failures
1647 List variable containing 2-tuples of TestCase instances and strings
1648 holding formatted tracebacks. Each tuple represents a test where
1649 a failure was explicitly signalled using the TestCase.assert*()
1650 methods.
1651 """
1652
juraj.linkes40dd73b2018-09-21 13:55:16 +02001653 failed_test_cases_info = set()
1654 core_crash_test_cases_info = set()
1655 current_test_case_info = None
1656
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001657 def __init__(self, stream=None, descriptions=None, verbosity=None, runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001658 """
Klement Sekerada505f62017-01-04 12:58:53 +01001659 :param stream File descriptor to store where to report test results.
1660 Set to the standard error stream by default.
1661 :param descriptions Boolean variable to store information if to use
1662 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001663 :param verbosity Integer variable to store required verbosity level.
1664 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001665 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001666 self.stream = stream
1667 self.descriptions = descriptions
1668 self.verbosity = verbosity
1669 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001670 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001671 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001672
Damjan Marionf56b77a2016-10-03 19:44:57 +02001673 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001674 """
1675 Record a test succeeded result
1676
1677 :param test:
1678
1679 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001680 if self.current_test_case_info:
1681 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001682 "--- addSuccess() %s.%s(%s) called"
1683 % (test.__class__.__name__, test._testMethodName, test._testMethodDoc)
1684 )
Damjan Marionf56b77a2016-10-03 19:44:57 +02001685 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001686 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001687
juraj.linkescae64f82018-09-19 15:01:47 +02001688 self.send_result_through_pipe(test, PASS)
1689
Klement Sekeraf62ae122016-10-11 11:47:09 +02001690 def addSkip(self, test, reason):
1691 """
1692 Record a test skipped.
1693
1694 :param test:
1695 :param reason:
1696
1697 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001698 if self.current_test_case_info:
1699 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001700 "--- addSkip() %s.%s(%s) called, reason is %s"
1701 % (
1702 test.__class__.__name__,
1703 test._testMethodName,
1704 test._testMethodDoc,
1705 reason,
1706 )
1707 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001708 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001709 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001710
Klement Sekera558ceab2021-04-08 19:37:41 +02001711 if reason == "not enough cpus":
1712 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1713 else:
1714 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001715
juraj.linkes40dd73b2018-09-21 13:55:16 +02001716 def symlink_failed(self):
1717 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001718 try:
Klement Sekerab23ffd72021-05-31 16:08:53 +02001719 failed_dir = config.failed_dir
juraj.linkes40dd73b2018-09-21 13:55:16 +02001720 link_path = os.path.join(
1721 failed_dir,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001722 "%s-FAILED" % os.path.basename(self.current_test_case_info.tempdir),
1723 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001724
1725 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001726 "creating a link to the failed test"
1727 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001728 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001729 "os.symlink(%s, %s)"
1730 % (self.current_test_case_info.tempdir, link_path)
1731 )
juraj.linkes184870a2018-07-16 14:22:01 +02001732 if os.path.exists(link_path):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001733 self.current_test_case_info.logger.debug("symlink already exists")
juraj.linkes184870a2018-07-16 14:22:01 +02001734 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001735 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001736
Klement Sekeraf413bef2017-08-15 07:09:02 +02001737 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001738 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001739
juraj.linkescae64f82018-09-19 15:01:47 +02001740 def send_result_through_pipe(self, test, result):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001741 if hasattr(self, "test_framework_result_pipe"):
juraj.linkescae64f82018-09-19 15:01:47 +02001742 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001743 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001744 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001745
juraj.linkes40dd73b2018-09-21 13:55:16 +02001746 def log_error(self, test, err, fn_name):
1747 if self.current_test_case_info:
1748 if isinstance(test, unittest.suite._ErrorHolder):
1749 test_name = test.description
1750 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001751 test_name = "%s.%s(%s)" % (
1752 test.__class__.__name__,
1753 test._testMethodName,
1754 test._testMethodDoc,
1755 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001756 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001757 "--- %s() %s called, err is %s" % (fn_name, test_name, err)
1758 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001759 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001760 "formatted exception is:\n%s" % "".join(format_exception(*err))
1761 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001762
1763 def add_error(self, test, err, unittest_fn, error_type):
1764 if error_type == FAIL:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001765 self.log_error(test, err, "addFailure")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001766 error_type_str = colorize("FAIL", RED)
1767 elif error_type == ERROR:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001768 self.log_error(test, err, "addError")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001769 error_type_str = colorize("ERROR", RED)
1770 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001771 raise Exception(
1772 "Error type %s cannot be used to record an "
1773 "error or a failure" % error_type
1774 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001775
1776 unittest_fn(self, test, err)
1777 if self.current_test_case_info:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001778 self.result_string = "%s [ temp dir used by test case: %s ]" % (
1779 error_type_str,
1780 self.current_test_case_info.tempdir,
1781 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001782 self.symlink_failed()
1783 self.failed_test_cases_info.add(self.current_test_case_info)
1784 if is_core_present(self.current_test_case_info.tempdir):
1785 if not self.current_test_case_info.core_crash_test:
1786 if isinstance(test, unittest.suite._ErrorHolder):
1787 test_name = str(test)
1788 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001789 test_name = "'{!s}' ({!s})".format(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001790 get_testcase_doc_name(test), test.id()
1791 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001792 self.current_test_case_info.core_crash_test = test_name
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001793 self.core_crash_test_cases_info.add(self.current_test_case_info)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001794 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001795 self.result_string = "%s [no temp dir]" % error_type_str
juraj.linkes40dd73b2018-09-21 13:55:16 +02001796
1797 self.send_result_through_pipe(test, error_type)
1798
Damjan Marionf56b77a2016-10-03 19:44:57 +02001799 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001800 """
1801 Record a test failed result
1802
1803 :param test:
1804 :param err: error message
1805
1806 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001807 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001808
Damjan Marionf56b77a2016-10-03 19:44:57 +02001809 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001810 """
1811 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001812
Klement Sekeraf62ae122016-10-11 11:47:09 +02001813 :param test:
1814 :param err: error message
1815
1816 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001817 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001818
Damjan Marionf56b77a2016-10-03 19:44:57 +02001819 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001820 """
1821 Get test description
1822
1823 :param test:
1824 :returns: test description
1825
1826 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001827 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001828
Damjan Marionf56b77a2016-10-03 19:44:57 +02001829 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001830 """
1831 Start a test
1832
1833 :param test:
1834
1835 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001836
1837 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001838 if test.__class__ in self.printed:
1839 return
1840
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001841 test_doc = getdoc(test)
1842 if not test_doc:
1843 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001844
Klement Sekeraea6236b2021-03-25 14:03:44 +01001845 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001846 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001847 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001848 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001849
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001850 # This block may overwrite the colorized title above,
1851 # but we want this to stand out and be fixed
1852 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001853 test_title = colorize(f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001854
Naveen Joy6eaeea92021-09-09 17:57:02 -07001855 if test.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001856 test_title = colorize(f"FIXME with ASAN: {test_title}", RED)
Naveen Joy6eaeea92021-09-09 17:57:02 -07001857 test.skip_fixme_asan()
1858
Dave Wallacee95b2462022-09-18 22:28:44 -04001859 if is_distro_ubuntu2204 == True and test.has_tag(
1860 TestCaseTag.FIXME_UBUNTU2204
1861 ):
1862 test_title = colorize(f"FIXME on Ubuntu-22.04: {test_title}", RED)
1863 test.skip_fixme_ubuntu2204()
1864
Dave Wallace670724c2022-09-20 21:52:18 -04001865 if is_distro_debian11 == True and test.has_tag(TestCaseTag.FIXME_DEBIAN11):
1866 test_title = colorize(f"FIXME on Debian-11: {test_title}", RED)
1867 test.skip_fixme_debian11()
1868
Dave Wallace8a0a9d22022-10-04 22:02:49 -04001869 if "debug" in config.vpp_tag and test.has_tag(TestCaseTag.FIXME_VPP_DEBUG):
1870 test_title = colorize(f"FIXME on VPP Debug: {test_title}", RED)
1871 test.skip_fixme_vpp_debug()
1872
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001873 if hasattr(test, "vpp_worker_count"):
Klement Sekeraea6236b2021-03-25 14:03:44 +01001874 if test.vpp_worker_count == 0:
1875 test_title += " [main thread only]"
1876 elif test.vpp_worker_count == 1:
1877 test_title += " [1 worker thread]"
1878 else:
1879 test_title += f" [{test.vpp_worker_count} worker threads]"
1880
Klement Sekera558ceab2021-04-08 19:37:41 +02001881 if test.__class__.skipped_due_to_cpu_lack:
1882 test_title = colorize(
1883 f"{test_title} [skipped - not enough cpus, "
1884 f"required={test.__class__.get_cpus_required()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001885 f"available={max_vpp_cpus}]",
1886 YELLOW,
1887 )
Klement Sekera558ceab2021-04-08 19:37:41 +02001888
1889 print(double_line_delim)
1890 print(test_title)
1891 print(double_line_delim)
1892 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001893
1894 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001895 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001896 unittest.TestResult.startTest(self, test)
1897 if self.verbosity > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001898 self.stream.writeln("Starting " + self.getDescription(test) + " ...")
Klement Sekeraf62ae122016-10-11 11:47:09 +02001899 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001900
Damjan Marionf56b77a2016-10-03 19:44:57 +02001901 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001902 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001903 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001904
1905 :param test:
1906
1907 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001908 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001909
Damjan Marionf56b77a2016-10-03 19:44:57 +02001910 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001911 self.stream.writeln(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001912 self.stream.writeln(
1913 "%-73s%s" % (self.getDescription(test), self.result_string)
1914 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001915 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001916 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001917 self.stream.writeln(
1918 "%-68s %4.2f %s"
1919 % (
1920 self.getDescription(test),
1921 time.time() - self.start_test,
1922 self.result_string,
1923 )
1924 )
juraj.linkescae64f82018-09-19 15:01:47 +02001925
1926 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001927
Damjan Marionf56b77a2016-10-03 19:44:57 +02001928 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001929 """
1930 Print errors from running the test case
1931 """
juraj.linkesabec0122018-11-16 17:28:56 +01001932 if len(self.errors) > 0 or len(self.failures) > 0:
1933 self.stream.writeln()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001934 self.printErrorList("ERROR", self.errors)
1935 self.printErrorList("FAIL", self.failures)
juraj.linkesabec0122018-11-16 17:28:56 +01001936
1937 # ^^ that is the last output from unittest before summary
1938 if not self.runner.print_summary:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001939 devnull = unittest.runner._WritelnDecorator(open(os.devnull, "w"))
juraj.linkesabec0122018-11-16 17:28:56 +01001940 self.stream = devnull
1941 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001942
Damjan Marionf56b77a2016-10-03 19:44:57 +02001943 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001944 """
1945 Print error list to the output stream together with error type
1946 and test case description.
1947
1948 :param flavour: error type
1949 :param errors: iterable errors
1950
1951 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001952 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001953 self.stream.writeln(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001954 self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001955 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001956 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001957
1958
Damjan Marionf56b77a2016-10-03 19:44:57 +02001959class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001960 """
Klement Sekera104543f2017-02-03 07:29:43 +01001961 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001962 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001963
Klement Sekeraf62ae122016-10-11 11:47:09 +02001964 @property
1965 def resultclass(self):
1966 """Class maintaining the results of the tests"""
1967 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001968
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001969 def __init__(
1970 self,
1971 keep_alive_pipe=None,
1972 descriptions=True,
1973 verbosity=1,
1974 result_pipe=None,
1975 failfast=False,
1976 buffer=False,
1977 resultclass=None,
1978 print_summary=True,
1979 **kwargs,
1980 ):
Klement Sekera7a161da2017-01-17 13:42:48 +01001981 # ignore stream setting here, use hard-coded stdout to be in sync
1982 # with prints from VppTestCase methods ...
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001983 super(VppTestRunner, self).__init__(
1984 sys.stdout, descriptions, verbosity, failfast, buffer, resultclass, **kwargs
1985 )
juraj.linkesccfead62018-11-21 13:20:43 +01001986 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001987
juraj.linkesabec0122018-11-16 17:28:56 +01001988 self.orig_stream = self.stream
1989 self.resultclass.test_framework_result_pipe = result_pipe
1990
1991 self.print_summary = print_summary
1992
1993 def _makeResult(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001994 return self.resultclass(self.stream, self.descriptions, self.verbosity, self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001995
Damjan Marionf56b77a2016-10-03 19:44:57 +02001996 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001997 """
1998 Run the tests
1999
2000 :param test:
2001
2002 """
Klement Sekera3658adc2017-06-07 08:19:47 +02002003 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02002004
2005 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01002006 if not self.print_summary:
2007 self.stream = self.orig_stream
2008 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02002009 return result
Neale Ranns812ed392017-10-16 04:20:13 -07002010
2011
2012class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002013 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
2014 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07002015 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002016 self.args = executable_args
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002017 if hasattr(self, "testcase") and self.testcase.debug_all:
Dave Wallace24564332019-10-21 02:53:14 +00002018 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002019 self.args = [
2020 "/usr/bin/gdbserver",
2021 "localhost:{port}".format(port=self.testcase.gdbserver_port),
2022 ] + args
2023 elif self.testcase.debug_gdb and hasattr(self, "wait_for_gdb"):
Dave Wallace24564332019-10-21 02:53:14 +00002024 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05002025 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00002026 self.app_name = os.path.basename(self.app_bin)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002027 if hasattr(self, "role"):
2028 self.app_name += " {role}".format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002029 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07002030 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002031 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05002032 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07002033
Dave Wallace24564332019-10-21 02:53:14 +00002034 def wait_for_enter(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002035 if not hasattr(self, "testcase"):
Dave Wallace24564332019-10-21 02:53:14 +00002036 return
2037 if self.testcase.debug_all and self.testcase.debug_gdbserver:
2038 print()
2039 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002040 print(
2041 "Spawned GDB Server for '{app}' with PID: {pid}".format(
2042 app=self.app_name, pid=self.process.pid
2043 )
2044 )
Dave Wallace24564332019-10-21 02:53:14 +00002045 elif self.testcase.debug_all and self.testcase.debug_gdb:
2046 print()
2047 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002048 print(
2049 "Spawned '{app}' with PID: {pid}".format(
2050 app=self.app_name, pid=self.process.pid
2051 )
2052 )
Dave Wallace24564332019-10-21 02:53:14 +00002053 else:
2054 return
2055 print(single_line_delim)
2056 print("You can debug '{app}' using:".format(app=self.app_name))
2057 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002058 print(
2059 "sudo gdb "
2060 + self.app_bin
2061 + " -ex 'target remote localhost:{port}'".format(
2062 port=self.testcase.gdbserver_port
2063 )
2064 )
2065 print(
2066 "Now is the time to attach gdb by running the above "
2067 "command, set up breakpoints etc., then resume from "
2068 "within gdb by issuing the 'continue' command"
2069 )
Dave Wallace24564332019-10-21 02:53:14 +00002070 self.testcase.gdbserver_port += 1
2071 elif self.testcase.debug_gdb:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002072 print(
2073 "sudo gdb "
2074 + self.app_bin
2075 + " -ex 'attach {pid}'".format(pid=self.process.pid)
2076 )
2077 print(
2078 "Now is the time to attach gdb by running the above "
2079 "command and set up breakpoints etc., then resume from"
2080 " within gdb by issuing the 'continue' command"
2081 )
Dave Wallace24564332019-10-21 02:53:14 +00002082 print(single_line_delim)
2083 input("Press ENTER to continue running the testcase...")
2084
Neale Ranns812ed392017-10-16 04:20:13 -07002085 def run(self):
2086 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002087 if not os.path.exists(executable) or not os.access(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002088 executable, os.F_OK | os.X_OK
2089 ):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04002090 # Exit code that means some system file did not exist,
2091 # could not be opened, or had some other kind of error.
2092 self.result = os.EX_OSFILE
2093 raise EnvironmentError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002094 "executable '%s' is not found or executable." % executable
2095 )
2096 self.logger.debug(
2097 "Running executable '{app}': '{cmd}'".format(
2098 app=self.app_name, cmd=" ".join(self.args)
2099 )
2100 )
Neale Ranns812ed392017-10-16 04:20:13 -07002101 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05002102 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07002103 env["CK_LOG_FILE_NAME"] = "-"
2104 self.process = subprocess.Popen(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002105 ["stdbuf", "-o0", "-e0"] + self.args,
2106 shell=False,
2107 env=env,
2108 preexec_fn=os.setpgrp,
2109 stdout=subprocess.PIPE,
2110 stderr=subprocess.PIPE,
2111 )
Dave Wallace24564332019-10-21 02:53:14 +00002112 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07002113 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00002114 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07002115 self.logger.info("Return code is `%s'" % self.process.returncode)
2116 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002117 self.logger.info(
2118 "Executable `{app}' wrote to stdout:".format(app=self.app_name)
2119 )
Neale Ranns812ed392017-10-16 04:20:13 -07002120 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002121 self.logger.info(out.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002122 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002123 self.logger.info(
2124 "Executable `{app}' wrote to stderr:".format(app=self.app_name)
2125 )
Neale Ranns812ed392017-10-16 04:20:13 -07002126 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002127 self.logger.info(err.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002128 self.logger.info(single_line_delim)
2129 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002130
Klement Sekera6aa58b72019-05-16 14:34:55 +02002131
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002132if __name__ == "__main__":
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002133 pass