blob: bfcc03086e5814ea55b8e6a703b1eebcce4e46f0 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05004import logging
Paul Vinciguerra72f00042018-11-25 11:05:13 -08005import sys
Ole Trøan162989e2018-11-26 10:27:50 +00006import os
7import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04008import signal
Paul Vinciguerrad6f22172020-12-05 22:39:14 +00009import subprocess
Ole Trøan162989e2018-11-26 10:27:50 +000010import unittest
Klement Sekerab23ffd72021-05-31 16:08:53 +020011import re
Klement Sekera277b89c2016-10-28 13:20:27 +020012import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080013import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000014import random
15import copy
juraj.linkes68ebc832018-11-29 09:37:08 +010016import platform
Klement Sekerab23ffd72021-05-31 16:08:53 +020017import shutil
Ole Trøan162989e2018-11-26 10:27:50 +000018from collections import deque
19from threading import Thread, Event
20from inspect import getdoc, isclass
21from traceback import format_exception
22from logging import FileHandler, DEBUG, Formatter
Andrew Yourtchenko06f32812021-01-14 10:19:08 +000023from enum import Enum
Klement Sekera558ceab2021-04-08 19:37:41 +020024from abc import ABC, abstractmethod
Ray Kinsellab8165b92021-09-22 11:24:06 +010025from struct import pack, unpack
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070026
27import scapy.compat
Klement Sekera56c492a2022-01-10 21:57:27 +000028from scapy.packet import Raw, Packet
Klement Sekerab23ffd72021-05-31 16:08:53 +020029from config import config, available_cpus, num_cpus, max_vpp_cpus
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040030import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080031from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010032from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000033from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070034from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000035from vpp_papi_provider import VppPapiProvider
Neale Ranns6197cb72021-06-03 14:43:21 +000036from vpp_papi import VppEnum
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050037import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000038from vpp_papi.vpp_stats import VPPStats
Ole Troan4376ab22021-03-03 10:40:05 +010039from vpp_papi.vpp_transport_socket import VppTransportSocketIOError
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020040from log import (
41 RED,
42 GREEN,
43 YELLOW,
44 double_line_delim,
45 single_line_delim,
46 get_logger,
47 colorize,
48)
Ole Trøan162989e2018-11-26 10:27:50 +000049from vpp_object import VppObjectRegistry
50from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020051from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
52from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
53from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080054
Klement Sekera558ceab2021-04-08 19:37:41 +020055
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050056logger = logging.getLogger(__name__)
57
58# Set up an empty logger for the testcase that can be overridden as necessary
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020059null_logger = logging.getLogger("VppTestCase")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050060null_logger.addHandler(logging.NullHandler())
61
juraj.linkescae64f82018-09-19 15:01:47 +020062PASS = 0
63FAIL = 1
64ERROR = 2
65SKIP = 3
66TEST_RUN = 4
Klement Sekera558ceab2021-04-08 19:37:41 +020067SKIP_CPU_SHORTAGE = 5
juraj.linkescae64f82018-09-19 15:01:47 +020068
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040069
Klement Sekerab23ffd72021-05-31 16:08:53 +020070if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010071 import debug_internal
72
Klement Sekeraf62ae122016-10-11 11:47:09 +020073"""
74 Test framework module.
75
76 The module provides a set of tools for constructing and running tests and
77 representing the results.
78"""
79
Klement Sekeraf62ae122016-10-11 11:47:09 +020080
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040081class VppDiedError(Exception):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020082 """exception for reporting that the subprocess has died."""
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040083
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020084 signals_by_value = {
85 v: k
86 for k, v in signal.__dict__.items()
87 if k.startswith("SIG") and not k.startswith("SIG_")
88 }
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040089
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040090 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040091 self.rv = rv
92 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040093 self.testcase = testcase
94 self.method_name = method_name
95
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040096 try:
97 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -040098 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040099 pass
100
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400101 if testcase is None and method_name is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200102 in_msg = ""
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400103 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200104 in_msg = " while running %s.%s" % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400105
Klement Sekera79a31db2021-03-12 18:16:10 +0100106 if self.rv:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200107 msg = "VPP subprocess died unexpectedly%s with return code: %d%s." % (
108 in_msg,
109 self.rv,
110 " [%s]" % (self.signal_name if self.signal_name is not None else ""),
111 )
Klement Sekera79a31db2021-03-12 18:16:10 +0100112 else:
113 msg = "VPP subprocess died unexpectedly%s." % in_msg
114
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400115 super(VppDiedError, self).__init__(msg)
116
117
Damjan Marionf56b77a2016-10-03 19:44:57 +0200118class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200119 """Private class to create packet info object.
120
121 Help process information about the next packet.
122 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200123 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200124
Matej Klotton86d87c42016-11-11 11:38:55 +0100125 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200126 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100127 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200128 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100129 #: Store the index of the destination packet generator interface
130 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200131 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100132 #: Store expected ip version
133 ip = -1
134 #: Store expected upper protocol
135 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100136 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200137 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200138
Matej Klotton16a14cd2016-12-07 15:09:13 +0100139 def __eq__(self, other):
140 index = self.index == other.index
141 src = self.src == other.src
142 dst = self.dst == other.dst
143 data = self.data == other.data
144 return index and src and dst and data
145
Klement Sekeraf62ae122016-10-11 11:47:09 +0200146
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100147def pump_output(testclass):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200148 """pump output from vpp stdout/stderr to proper queues"""
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100149 stdout_fragment = ""
150 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400151 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200152 readable = select.select(
153 [
154 testclass.vpp.stdout.fileno(),
155 testclass.vpp.stderr.fileno(),
156 testclass.pump_thread_wakeup_pipe[0],
157 ],
158 [],
159 [],
160 )[0]
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100161 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100162 read = os.read(testclass.vpp.stdout.fileno(), 102400)
163 if len(read) > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200164 split = read.decode("ascii", errors="backslashreplace").splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100165 if len(stdout_fragment) > 0:
166 split[0] = "%s%s" % (stdout_fragment, split[0])
167 if len(split) > 0 and split[-1].endswith("\n"):
168 limit = None
169 else:
170 limit = -1
171 stdout_fragment = split[-1]
172 testclass.vpp_stdout_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200173 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100174 for line in split[:limit]:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200175 testclass.logger.info("VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100176 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100177 read = os.read(testclass.vpp.stderr.fileno(), 102400)
178 if len(read) > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200179 split = read.decode("ascii", errors="backslashreplace").splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100180 if len(stderr_fragment) > 0:
181 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200182 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100183 limit = None
184 else:
185 limit = -1
186 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200187
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100188 testclass.vpp_stderr_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200189 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100190 for line in split[:limit]:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200191 testclass.logger.error("VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800192 # ignoring the dummy pipe here intentionally - the
193 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200194
195
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800196def _is_platform_aarch64():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200197 return platform.machine() == "aarch64"
juraj.linkes68ebc832018-11-29 09:37:08 +0100198
Klement Sekera6aa58b72019-05-16 14:34:55 +0200199
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800200is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100201
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800202
Klement Sekera909a6a12017-08-08 04:33:53 +0200203class KeepAliveReporter(object):
204 """
205 Singleton object which reports test start to parent process
206 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200207
Klement Sekera909a6a12017-08-08 04:33:53 +0200208 _shared_state = {}
209
210 def __init__(self):
211 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800212 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200213
214 @property
215 def pipe(self):
216 return self._pipe
217
218 @pipe.setter
219 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800220 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200221 raise Exception("Internal error - pipe should only be set once.")
222 self._pipe = pipe
223
juraj.linkes40dd73b2018-09-21 13:55:16 +0200224 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200225 """
226 Write current test tmpdir & desc to keep-alive pipe to signal liveness
227 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200228 if self.pipe is None:
229 # if not running forked..
230 return
231
Klement Sekera909a6a12017-08-08 04:33:53 +0200232 if isclass(test):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200233 desc = "%s (%s)" % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200234 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200235 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200236
Klement Sekerab23ffd72021-05-31 16:08:53 +0200237 self.pipe.send((desc, config.vpp, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200238
239
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000240class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000241 # marks the suites that must run at the end
242 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000243 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000244 # marks the suites broken on VPP multi-worker
245 FIXME_VPP_WORKERS = 2
Naveen Joy6eaeea92021-09-09 17:57:02 -0700246 # marks the suites broken when ASan is enabled
247 FIXME_ASAN = 3
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000248
249
250def create_tag_decorator(e):
251 def decorator(cls):
252 try:
253 cls.test_tags.append(e)
254 except AttributeError:
255 cls.test_tags = [e]
256 return cls
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200257
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000258 return decorator
259
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000260
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000261tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000262tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Naveen Joy6eaeea92021-09-09 17:57:02 -0700263tag_fixme_asan = create_tag_decorator(TestCaseTag.FIXME_ASAN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000264
265
Klement Sekerae2636852021-03-16 12:52:12 +0100266class DummyVpp:
267 returncode = None
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200268 pid = 0xCAFEBAFE
Klement Sekerae2636852021-03-16 12:52:12 +0100269
270 def poll(self):
271 pass
272
273 def terminate(self):
274 pass
275
276
Klement Sekera558ceab2021-04-08 19:37:41 +0200277class CPUInterface(ABC):
278 cpus = []
279 skipped_due_to_cpu_lack = False
280
281 @classmethod
282 @abstractmethod
283 def get_cpus_required(cls):
284 pass
285
286 @classmethod
287 def assign_cpus(cls, cpus):
288 cls.cpus = cpus
289
290
291class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100292 """This subclass is a base class for VPP test cases that are implemented as
293 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200294 """
295
Arthur de Kerhordb023802021-03-11 10:26:54 -0800296 extra_vpp_statseg_config = ""
Ole Troana45dc072018-12-21 16:04:22 +0100297 extra_vpp_punt_config = []
298 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500299 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400300 vapi_response_timeout = 5
Klement Sekera140af152022-02-18 10:30:51 +0000301 remove_configured_vpp_objects_on_tear_down = True
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100302
Klement Sekeraf62ae122016-10-11 11:47:09 +0200303 @property
304 def packet_infos(self):
305 """List of packet infos"""
306 return self._packet_infos
307
Klement Sekeradab231a2016-12-21 08:50:14 +0100308 @classmethod
309 def get_packet_count_for_if_idx(cls, dst_if_index):
310 """Get the number of packet info for specified destination if index"""
311 if dst_if_index in cls._packet_count_for_dst_if_idx:
312 return cls._packet_count_for_dst_if_idx[dst_if_index]
313 else:
314 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200315
316 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000317 def has_tag(cls, tag):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200318 """if the test case has a given tag - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000319 try:
320 return tag in cls.test_tags
321 except AttributeError:
322 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000323 return False
324
325 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000326 def is_tagged_run_solo(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200327 """if the test case class is timing-sensitive - return true"""
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000328 return cls.has_tag(TestCaseTag.RUN_SOLO)
329
330 @classmethod
Naveen Joy6eaeea92021-09-09 17:57:02 -0700331 def skip_fixme_asan(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200332 """if @tag_fixme_asan & ASan is enabled - mark for skip"""
Naveen Joy6eaeea92021-09-09 17:57:02 -0700333 if cls.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200334 vpp_extra_cmake_args = os.environ.get("VPP_EXTRA_CMAKE_ARGS", "")
335 if "DVPP_ENABLE_SANITIZE_ADDR=ON" in vpp_extra_cmake_args:
Naveen Joy6eaeea92021-09-09 17:57:02 -0700336 cls = unittest.skip("Skipping @tag_fixme_asan tests")(cls)
337
338 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200339 def instance(cls):
340 """Return the instance of this testcase"""
341 return cls.test_instance
342
Damjan Marionf56b77a2016-10-03 19:44:57 +0200343 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200344 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000345 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200346 cls.debug_core = False
347 cls.debug_gdb = False
348 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000349 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100350 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200351 if d is None:
352 return
353 dl = d.lower()
354 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200355 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000356 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200357 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000358 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200359 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100360 elif dl == "attach":
361 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200362 else:
363 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000364 if dl == "gdb-all" or dl == "gdbserver-all":
365 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200366
Klement Sekera558ceab2021-04-08 19:37:41 +0200367 @classmethod
368 def get_vpp_worker_count(cls):
369 if not hasattr(cls, "vpp_worker_count"):
370 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
371 cls.vpp_worker_count = 0
372 else:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200373 cls.vpp_worker_count = config.vpp_worker_count
Klement Sekera558ceab2021-04-08 19:37:41 +0200374 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200375
Klement Sekera558ceab2021-04-08 19:37:41 +0200376 @classmethod
377 def get_cpus_required(cls):
378 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200379
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800380 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200381 def setUpConstants(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200382 """Set-up the test case class based on environment variables"""
Klement Sekerab23ffd72021-05-31 16:08:53 +0200383 cls.step = config.step
384 cls.plugin_path = ":".join(config.vpp_plugin_dir)
385 cls.test_plugin_path = ":".join(config.vpp_test_plugin_dir)
386 cls.extern_plugin_path = ":".join(config.extern_plugin_dir)
Ole Troana45dc072018-12-21 16:04:22 +0100387 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100388 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100389 debug_cli = "cli-listen localhost:5002"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200390 size = re.search(r"\d+[gG]", config.coredump_size)
391 if size:
392 coredump_size = f"coredump-size {config.coredump_size}".lower()
393 else:
Ole Troana45dc072018-12-21 16:04:22 +0100394 coredump_size = "coredump-size unlimited"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200395 default_variant = config.variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000396 if default_variant is not None:
Benoît Ganne09ef5922022-07-29 10:52:34 +0200397 default_variant = "default { variant %s 100 }" % default_variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000398 else:
399 default_variant = ""
400
Klement Sekerab23ffd72021-05-31 16:08:53 +0200401 api_fuzzing = config.api_fuzz
Dave Barach77841402020-04-29 17:04:10 -0400402 if api_fuzzing is None:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200403 api_fuzzing = "off"
Dave Barach77841402020-04-29 17:04:10 -0400404
Klement Sekera8d815022021-03-15 16:58:10 +0100405 cls.vpp_cmdline = [
Klement Sekerab23ffd72021-05-31 16:08:53 +0200406 config.vpp,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200407 "unix",
408 "{",
409 "nodaemon",
410 debug_cli,
411 "full-coredump",
412 coredump_size,
413 "runtime-dir",
414 cls.tempdir,
Klement Sekera8d815022021-03-15 16:58:10 +0100415 "}",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200416 "api-trace",
417 "{",
418 "on",
419 "}",
420 "api-segment",
421 "{",
422 "prefix",
423 cls.get_api_segment_prefix(),
424 "}",
425 "cpu",
426 "{",
427 "main-core",
428 str(cls.cpus[0]),
429 ]
430 if cls.extern_plugin_path not in (None, ""):
431 cls.extra_vpp_plugin_config.append("add-path %s" % cls.extern_plugin_path)
432 if cls.get_vpp_worker_count():
433 cls.vpp_cmdline.extend(
434 ["corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])]
435 )
436 cls.vpp_cmdline.extend(
437 [
438 "}",
439 "physmem",
440 "{",
441 "max-size",
442 "32m",
443 "}",
444 "statseg",
445 "{",
446 "socket-name",
447 cls.get_stats_sock_path(),
448 cls.extra_vpp_statseg_config,
449 "}",
450 "socksvr",
451 "{",
452 "socket-name",
453 cls.get_api_sock_path(),
454 "}",
455 "node { ",
456 default_variant,
457 "}",
458 "api-fuzz {",
459 api_fuzzing,
460 "}",
461 "plugins",
462 "{",
463 "plugin",
464 "dpdk_plugin.so",
465 "{",
466 "disable",
467 "}",
468 "plugin",
469 "rdma_plugin.so",
470 "{",
471 "disable",
472 "}",
473 "plugin",
474 "lisp_unittest_plugin.so",
475 "{",
476 "enable",
477 "}",
478 "plugin",
479 "unittest_plugin.so",
480 "{",
481 "enable",
482 "}",
483 ]
484 + cls.extra_vpp_plugin_config
485 + [
486 "}",
487 ]
488 )
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000489
Ole Troana45dc072018-12-21 16:04:22 +0100490 if cls.extra_vpp_punt_config is not None:
491 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Dave Barach7d31ab22019-05-08 19:18:18 -0400492
Klement Sekerae2636852021-03-16 12:52:12 +0100493 if not cls.debug_attach:
494 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
495 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200496
497 @classmethod
498 def wait_for_enter(cls):
499 if cls.debug_gdbserver:
500 print(double_line_delim)
501 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
502 elif cls.debug_gdb:
503 print(double_line_delim)
504 print("Spawned VPP with PID: %d" % cls.vpp.pid)
505 else:
506 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
507 return
508 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000509 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200510 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200511 print(
512 f"sudo gdb {config.vpp} "
513 f"-ex 'target remote localhost:{cls.gdbserver_port}'"
514 )
515 print(
516 "Now is the time to attach gdb by running the above "
517 "command, set up breakpoints etc., then resume VPP from "
518 "within gdb by issuing the 'continue' command"
519 )
Dave Wallace24564332019-10-21 02:53:14 +0000520 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200521 elif cls.debug_gdb:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200522 print(f"sudo gdb {config.vpp} -ex 'attach {cls.vpp.pid}'")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200523 print(
524 "Now is the time to attach gdb by running the above "
525 "command and set up breakpoints etc., then resume VPP from"
526 " within gdb by issuing the 'continue' command"
527 )
Klement Sekera277b89c2016-10-28 13:20:27 +0200528 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800529 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200530
531 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100532 def attach_vpp(cls):
533 cls.vpp = DummyVpp()
534
535 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200536 def run_vpp(cls):
Klement Sekera558ceab2021-04-08 19:37:41 +0200537 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200538 cmdline = cls.vpp_cmdline
539
540 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200541 gdbserver = "/usr/bin/gdbserver"
542 if not os.path.isfile(gdbserver) or not os.access(gdbserver, os.X_OK):
543 raise Exception(
544 "gdbserver binary '%s' does not exist or is "
545 "not executable" % gdbserver
546 )
Klement Sekera931be3a2016-11-03 05:36:01 +0100547
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200548 cmdline = [
549 gdbserver,
550 "localhost:{port}".format(port=cls.gdbserver_port),
551 ] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200552 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
553
Klement Sekera931be3a2016-11-03 05:36:01 +0100554 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200555 cls.vpp = subprocess.Popen(
556 cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE
557 )
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800558 except subprocess.CalledProcessError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200559 cls.logger.critical(
560 "Subprocess returned with non-0 return code: (%s)", e.returncode
561 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800562 raise
563 except OSError as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200564 cls.logger.critical(
565 "Subprocess returned with OS error: (%s) %s", e.errno, e.strerror
566 )
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800567 raise
568 except Exception as e:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200569 cls.logger.exception("Subprocess returned unexpected from %s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100570 raise
571
Klement Sekera277b89c2016-10-28 13:20:27 +0200572 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100573
Damjan Marionf56b77a2016-10-03 19:44:57 +0200574 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000575 def wait_for_coredump(cls):
576 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400577 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000578 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400579 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000580 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400581 ok = False
582 while time.time() < deadline:
583 cls.sleep(1)
584 size = curr_size
585 curr_size = os.path.getsize(corefile)
586 if size == curr_size:
587 ok = True
588 break
589 if not ok:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200590 cls.logger.error(
591 "Timed out waiting for coredump to complete: %s", corefile
592 )
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400593 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200594 cls.logger.error("Coredump complete: %s, size %d", corefile, curr_size)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400595
596 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100597 def get_stats_sock_path(cls):
598 return "%s/stats.sock" % cls.tempdir
599
600 @classmethod
601 def get_api_sock_path(cls):
602 return "%s/api.sock" % cls.tempdir
603
604 @classmethod
605 def get_api_segment_prefix(cls):
606 return os.path.basename(cls.tempdir) # Only used for VAPI
607
608 @classmethod
609 def get_tempdir(cls):
Klement Sekerab3fc6582022-03-10 11:47:45 +0100610 if cls.debug_attach:
611 tmpdir = f"{config.tmp_dir}/unittest-attach-gdb"
612 else:
613 tmpdir = f"{config.tmp_dir}/vpp-unittest-{cls.__name__}"
614 if config.wipe_tmp_dir:
615 shutil.rmtree(tmpdir, ignore_errors=True)
616 os.mkdir(tmpdir)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200617 return tmpdir
618
619 @classmethod
620 def create_file_handler(cls):
621 if config.log_dir is None:
622 cls.file_handler = FileHandler(f"{cls.tempdir}/log.txt")
623 return
624
625 logdir = f"{config.log_dir}/vpp-unittest-{cls.__name__}"
626 if config.wipe_tmp_dir:
627 shutil.rmtree(logdir, ignore_errors=True)
628 os.mkdir(logdir)
629 cls.file_handler = FileHandler(f"{logdir}/log.txt")
Klement Sekerae2636852021-03-16 12:52:12 +0100630
631 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200632 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200633 """
634 Perform class setup before running the testcase
635 Remove shared memory files, start vpp and connect the vpp-api
636 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800637 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100638 cls.logger = get_logger(cls.__name__)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200639 random.seed(config.rnd_seed)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200640 if hasattr(cls, "parallel_handler"):
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100641 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100642 cls.logger.propagate = False
Klement Sekerab23ffd72021-05-31 16:08:53 +0200643 cls.set_debug_flags(config.debug)
Klement Sekerae2636852021-03-16 12:52:12 +0100644 cls.tempdir = cls.get_tempdir()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200645 cls.create_file_handler()
Klement Sekera027dbd52017-04-11 06:01:53 +0200646 cls.file_handler.setFormatter(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200647 Formatter(fmt="%(asctime)s,%(msecs)03d %(message)s", datefmt="%H:%M:%S")
648 )
Klement Sekera027dbd52017-04-11 06:01:53 +0200649 cls.file_handler.setLevel(DEBUG)
650 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100651 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200652 os.chdir(cls.tempdir)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200653 cls.logger.info(
654 "Temporary dir is %s, api socket is %s",
655 cls.tempdir,
656 cls.get_api_sock_path(),
657 )
Klement Sekerab23ffd72021-05-31 16:08:53 +0200658 cls.logger.debug("Random seed is %s", config.rnd_seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200659 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100660 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200661 cls._pcaps = []
662 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200663 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100664 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100665 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200666 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200667 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200668 # need to catch exceptions here because if we raise, then the cleanup
669 # doesn't get called and we might end with a zombie vpp
670 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100671 if cls.debug_attach:
672 cls.attach_vpp()
673 else:
674 cls.run_vpp()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200675 cls.reporter.send_keep_alive(cls, "setUpClass")
juraj.linkes40dd73b2018-09-21 13:55:16 +0200676 VppTestResult.current_test_case_info = TestCaseInfo(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200677 cls.logger, cls.tempdir, cls.vpp.pid, config.vpp
678 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100679 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100680 cls.vpp_stderr_deque = deque()
Klement Sekerae2636852021-03-16 12:52:12 +0100681 if not cls.debug_attach:
682 cls.pump_thread_stop_flag = Event()
683 cls.pump_thread_wakeup_pipe = os.pipe()
684 cls.pump_thread = Thread(target=pump_output, args=(cls,))
685 cls.pump_thread.daemon = True
686 cls.pump_thread.start()
687 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400688 cls.vapi_response_timeout = 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200689 cls.vapi = VppPapiProvider(cls.__name__, cls, cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100690 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400691 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100692 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400693 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100694 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100695 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200696 try:
697 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100698 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200699 cls.vpp_startup_failed = True
700 cls.logger.critical(
701 "VPP died shortly after startup, check the"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200702 " output to standard error for possible cause"
703 )
Klement Sekera3747c752017-04-10 06:30:17 +0200704 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100705 try:
706 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100707 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500708 cls.logger.debug("Exception connecting to vapi: %s" % e)
709 cls.vapi.disconnect()
710
Klement Sekera085f5c02016-11-24 01:59:16 +0100711 if cls.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200712 print(
713 colorize(
714 "You're running VPP inside gdbserver but "
715 "VPP-API connection failed, did you forget "
716 "to 'continue' VPP from within gdb?",
717 RED,
718 )
719 )
Ole Troan4376ab22021-03-03 10:40:05 +0100720 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100721 if cls.debug_attach:
722 last_line = cls.vapi.cli("show thread").split("\n")[-2]
723 cls.vpp_worker_count = int(last_line.split(" ")[0])
724 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400725 except vpp_papi.VPPRuntimeError as e:
726 cls.logger.debug("%s" % e)
727 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100728 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000729 except Exception as e:
730 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400731 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100732 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200733
Damjan Marionf56b77a2016-10-03 19:44:57 +0200734 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500735 def _debug_quit(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200736 if cls.debug_gdbserver or cls.debug_gdb:
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500737 try:
738 cls.vpp.poll()
739
740 if cls.vpp.returncode is None:
741 print()
742 print(double_line_delim)
743 print("VPP or GDB server is still running")
744 print(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200745 input(
746 "When done debugging, press ENTER to kill the "
747 "process and finish running the testcase..."
748 )
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500749 except AttributeError:
750 pass
751
752 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200753 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200754 """
755 Disconnect vpp-api, kill vpp and cleanup shared memory files
756 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500757 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200758
juraj.linkes184870a2018-07-16 14:22:01 +0200759 # first signal that we want to stop the pump thread, then wake it up
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200760 if hasattr(cls, "pump_thread_stop_flag"):
juraj.linkes184870a2018-07-16 14:22:01 +0200761 cls.pump_thread_stop_flag.set()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200762 if hasattr(cls, "pump_thread_wakeup_pipe"):
763 os.write(cls.pump_thread_wakeup_pipe[1], b"ding dong wake up")
764 if hasattr(cls, "pump_thread"):
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100765 cls.logger.debug("Waiting for pump thread to stop")
766 cls.pump_thread.join()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200767 if hasattr(cls, "vpp_stderr_reader_thread"):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500768 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100769 cls.vpp_stderr_reader_thread.join()
770
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200771 if hasattr(cls, "vpp"):
772 if hasattr(cls, "vapi"):
Ole Troanfd574082019-11-27 23:12:48 +0100773 cls.logger.debug(cls.vapi.vpp.get_stats())
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200774 cls.logger.debug("Disconnecting class vapi client on %s", cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100775 cls.vapi.disconnect()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200776 cls.logger.debug("Deleting class vapi attribute on %s", cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100777 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200778 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100779 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000780 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100781 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400782 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100783 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100784 try:
785 outs, errs = cls.vpp.communicate(timeout=5)
786 except subprocess.TimeoutExpired:
787 cls.vpp.kill()
788 outs, errs = cls.vpp.communicate()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200789 cls.logger.debug("Deleting class vpp attribute on %s", cls.__name__)
Klement Sekerae2636852021-03-16 12:52:12 +0100790 if not cls.debug_attach:
791 cls.vpp.stdout.close()
792 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200793 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200794
Klement Sekera3747c752017-04-10 06:30:17 +0200795 if cls.vpp_startup_failed:
796 stdout_log = cls.logger.info
797 stderr_log = cls.logger.critical
798 else:
799 stdout_log = cls.logger.info
800 stderr_log = cls.logger.info
801
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200802 if hasattr(cls, "vpp_stdout_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200803 stdout_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200804 stdout_log("VPP output to stdout while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200805 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100806 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200807 with open(cls.tempdir + "/vpp_stdout.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200808 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200809 stdout_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200810 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200811
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200812 if hasattr(cls, "vpp_stderr_deque"):
Klement Sekera3747c752017-04-10 06:30:17 +0200813 stderr_log(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200814 stderr_log("VPP output to stderr while running %s:", cls.__name__)
Klement Sekera3747c752017-04-10 06:30:17 +0200815 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100816 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200817 with open(cls.tempdir + "/vpp_stderr.txt", "w") as f:
Klement Sekera027dbd52017-04-11 06:01:53 +0200818 f.write(vpp_output)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200819 stderr_log("\n%s", vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200820 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200821
Damjan Marionf56b77a2016-10-03 19:44:57 +0200822 @classmethod
823 def tearDownClass(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200824 """Perform final cleanup after running all tests in this test-case"""
825 cls.logger.debug("--- tearDownClass() for %s called ---" % cls.__name__)
826 cls.reporter.send_keep_alive(cls, "tearDownClass")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200827 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200828 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100829 cls.reset_packet_infos()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200830 if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +0100831 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200832
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700833 def show_commands_at_teardown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200834 """Allow subclass specific teardown logging additions."""
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700835 self.logger.info("--- No test specific show commands provided. ---")
836
Damjan Marionf56b77a2016-10-03 19:44:57 +0200837 def tearDown(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200838 """Show various debug prints after each test"""
839 self.logger.debug(
840 "--- tearDown() for %s.%s(%s) called ---"
841 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
842 )
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700843
844 try:
845 if not self.vpp_dead:
846 self.logger.debug(self.vapi.cli("show trace max 1000"))
847 self.logger.info(self.vapi.ppcli("show interface"))
848 self.logger.info(self.vapi.ppcli("show hardware"))
849 self.logger.info(self.statistics.set_errors_str())
850 self.logger.info(self.vapi.ppcli("show run"))
851 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400852 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700853 self.logger.info("Logging testcase specific show commands.")
854 self.show_commands_at_teardown()
Klement Sekera140af152022-02-18 10:30:51 +0000855 if self.remove_configured_vpp_objects_on_tear_down:
856 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500857 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000858 m = self._testMethodName
859 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500860 tmp_api_trace = "/tmp/%s" % api_trace
861 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
862 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200863 self.logger.info("Moving %s to %s\n" % (tmp_api_trace, vpp_api_trace_log))
Dave Wallace90c55722017-02-16 11:25:26 -0500864 os.rename(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100865 except VppTransportSocketIOError:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200866 self.logger.debug(
867 "VppTransportSocketIOError: Vpp dead. Cannot log show commands."
868 )
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700869 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100870 else:
871 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200872
Damjan Marionf56b77a2016-10-03 19:44:57 +0200873 def setUp(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200874 """Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800875 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200876 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100877 if self.vpp_dead:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200878 raise VppDiedError(
879 rv=None,
880 testcase=self.__class__.__name__,
881 method_name=self._testMethodName,
882 )
883 self.sleep(0.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100884 self.vpp_stdout_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200885 "--- test setUp() for %s.%s(%s) starts here ---\n"
886 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
887 )
Klement Sekerae4504c62016-12-08 10:16:41 +0100888 self.vpp_stderr_deque.append(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200889 "--- test setUp() for %s.%s(%s) starts here ---\n"
890 % (self.__class__.__name__, self._testMethodName, self._testMethodDoc)
891 )
Klement Sekeraf62ae122016-10-11 11:47:09 +0200892 self.vapi.cli("clear trace")
893 # store the test instance inside the test class - so that objects
894 # holding the class can access instance methods (like assertEqual)
895 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200896
Damjan Marionf56b77a2016-10-03 19:44:57 +0200897 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200898 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200899 """
900 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200901
Klement Sekera75e7d132017-09-20 08:26:30 +0200902 :param interfaces: iterable interface indexes (if None,
903 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200904
Klement Sekeraf62ae122016-10-11 11:47:09 +0200905 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200906 if interfaces is None:
907 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200908 for i in interfaces:
909 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200910
Damjan Marionf56b77a2016-10-03 19:44:57 +0200911 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200912 def register_pcap(cls, intf, worker):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200913 """Register a pcap in the testclass"""
Klement Sekera9225dee2016-12-12 08:36:58 +0100914 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200915 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100916
917 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000918 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400919 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
920 # returns float("2.190522")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200921 timestr = cls.vapi.cli("show clock")
922 head, sep, tail = timestr.partition(",")
923 head, sep, tail = head.partition("Time now")
Dave Barach19718002020-03-11 10:31:36 -0400924 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000925
926 @classmethod
927 def sleep_on_vpp_time(cls, sec):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200928 """Sleep according to time in VPP world"""
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000929 # On a busy system with many processes
930 # we might end up with VPP time being slower than real world
931 # So take that into account when waiting for VPP to do something
932 start_time = cls.get_vpp_time()
933 while cls.get_vpp_time() - start_time < sec:
934 cls.sleep(0.1)
935
936 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100937 def pg_start(cls, trace=True):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200938 """Enable the PG, wait till it is done, then clean up"""
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200939 for (intf, worker) in cls._old_pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200940 intf.handle_old_pcap_file(intf.get_in_path(worker), intf.in_history_counter)
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200941 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100942 if trace:
943 cls.vapi.cli("clear trace")
944 cls.vapi.cli("trace add pg-input 1000")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200945 cls.vapi.cli("packet-generator enable")
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000946 # PG, when starts, runs to completion -
947 # so let's avoid a race condition,
948 # and wait a little till it's done.
949 # Then clean it up - and then be gone.
950 deadline = time.time() + 300
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200951 while cls.vapi.cli("show packet-generator").find("Yes") != -1:
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000952 cls.sleep(0.01) # yield
953 if time.time() > deadline:
954 cls.logger.error("Timeout waiting for pg to stop")
955 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200956 for intf, worker in cls._pcaps:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200957 cls.vapi.cli("packet-generator delete %s" % intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200958 cls._old_pcaps = cls._pcaps
959 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200960
Damjan Marionf56b77a2016-10-03 19:44:57 +0200961 @classmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200962 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0, mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200963 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100964 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200965
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100966 :param interfaces: iterable indexes of the interfaces.
967 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200968
Klement Sekeraf62ae122016-10-11 11:47:09 +0200969 """
970 result = []
971 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +0000972 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200973 setattr(cls, intf.name, intf)
974 result.append(intf)
975 cls.pg_interfaces = result
976 return result
977
Matej Klotton0178d522016-11-04 11:11:44 +0100978 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000979 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
980 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200981 return cls.create_pg_interfaces_internal(
982 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP4
983 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000984
985 @classmethod
986 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
987 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200988 return cls.create_pg_interfaces_internal(
989 interfaces, gso, gso_size, pgmode.PG_API_MODE_IP6
990 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000991
992 @classmethod
993 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
994 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200995 return cls.create_pg_interfaces_internal(
996 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
997 )
Neale Ranns6197cb72021-06-03 14:43:21 +0000998
999 @classmethod
1000 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
1001 pgmode = VppEnum.vl_api_pg_interface_mode_t
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001002 return cls.create_pg_interfaces_internal(
1003 interfaces, gso, gso_size, pgmode.PG_API_MODE_ETHERNET
1004 )
Neale Ranns6197cb72021-06-03 14:43:21 +00001005
1006 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +02001007 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +01001008 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001009 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001010
Klement Sekerab9ef2732018-06-24 22:49:33 +02001011 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +01001012 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +01001013 """
Klement Sekerab9ef2732018-06-24 22:49:33 +02001014 result = [VppLoInterface(cls) for i in range(count)]
1015 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +01001016 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +01001017 cls.lo_interfaces = result
1018 return result
1019
Neale Ranns192b13f2019-03-15 02:16:20 -07001020 @classmethod
1021 def create_bvi_interfaces(cls, count):
1022 """
1023 Create BVI interfaces.
1024
1025 :param count: number of interfaces created.
1026 :returns: List of created interfaces.
1027 """
1028 result = [VppBviInterface(cls) for i in range(count)]
1029 for intf in result:
1030 setattr(cls, intf.name, intf)
1031 cls.bvi_interfaces = result
1032 return result
1033
Damjan Marionf56b77a2016-10-03 19:44:57 +02001034 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001035 def extend_packet(packet, size, padding=" "):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001036 """
Klement Sekera75e7d132017-09-20 08:26:30 +02001037 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +02001038 NOTE: Currently works only when Raw layer is present.
1039
1040 :param packet: packet
1041 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +02001042 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +02001043
1044 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001045 packet_len = len(packet) + 4
1046 extend = size - packet_len
1047 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +02001048 num = (extend // len(padding)) + 1
1049 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +02001050
Klement Sekeradab231a2016-12-21 08:50:14 +01001051 @classmethod
1052 def reset_packet_infos(cls):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001053 """Reset the list of packet info objects and packet counts to zero"""
Klement Sekeradab231a2016-12-21 08:50:14 +01001054 cls._packet_infos = {}
1055 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +02001056
Klement Sekeradab231a2016-12-21 08:50:14 +01001057 @classmethod
1058 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001059 """
1060 Create packet info object containing the source and destination indexes
1061 and add it to the testcase's packet info list
1062
Klement Sekeradab231a2016-12-21 08:50:14 +01001063 :param VppInterface src_if: source interface
1064 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +02001065
1066 :returns: _PacketInfo object
1067
1068 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001069 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +01001070 info.index = len(cls._packet_infos)
1071 info.src = src_if.sw_if_index
1072 info.dst = dst_if.sw_if_index
1073 if isinstance(dst_if, VppSubInterface):
1074 dst_idx = dst_if.parent.sw_if_index
1075 else:
1076 dst_idx = dst_if.sw_if_index
1077 if dst_idx in cls._packet_count_for_dst_if_idx:
1078 cls._packet_count_for_dst_if_idx[dst_idx] += 1
1079 else:
1080 cls._packet_count_for_dst_if_idx[dst_idx] = 1
1081 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001082 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001083
Damjan Marionf56b77a2016-10-03 19:44:57 +02001084 @staticmethod
1085 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001086 """
1087 Convert _PacketInfo object to packet payload
1088
1089 :param info: _PacketInfo object
1090
1091 :returns: string containing serialized data from packet info
1092 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001093
1094 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001095 return pack("iiiih", info.index, info.src, info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001096
Damjan Marionf56b77a2016-10-03 19:44:57 +02001097 @staticmethod
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001098 def payload_to_info(payload, payload_field="load"):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001099 """
1100 Convert packet payload to _PacketInfo object
1101
1102 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001103 :type payload: <class 'scapy.packet.Raw'>
1104 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001105 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001106 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001107 :returns: _PacketInfo object containing de-serialized data from payload
1108
1109 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001110
1111 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1112 payload_b = getattr(payload, payload_field)[:18]
1113
Damjan Marionf56b77a2016-10-03 19:44:57 +02001114 info = _PacketInfo()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001115 info.index, info.src, info.dst, info.ip, info.proto = unpack("iiiih", payload_b)
Ray Kinsellab8165b92021-09-22 11:24:06 +01001116
1117 # some SRv6 TCs depend on get an exception if bad values are detected
1118 if info.index > 0x4000:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001119 raise ValueError("Index value is invalid")
Ray Kinsellab8165b92021-09-22 11:24:06 +01001120
Damjan Marionf56b77a2016-10-03 19:44:57 +02001121 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001122
Damjan Marionf56b77a2016-10-03 19:44:57 +02001123 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001124 """
1125 Iterate over the packet info list stored in the testcase
1126 Start iteration with first element if info is None
1127 Continue based on index in info if info is specified
1128
1129 :param info: info or None
1130 :returns: next info in list or None if no more infos
1131 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001132 if info is None:
1133 next_index = 0
1134 else:
1135 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001136 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001137 return None
1138 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001139 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001140
Klement Sekeraf62ae122016-10-11 11:47:09 +02001141 def get_next_packet_info_for_interface(self, src_index, info):
1142 """
1143 Search the packet info list for the next packet info with same source
1144 interface index
1145
1146 :param src_index: source interface index to search for
1147 :param info: packet info - where to start the search
1148 :returns: packet info or None
1149
1150 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001151 while True:
1152 info = self.get_next_packet_info(info)
1153 if info is None:
1154 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001155 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001156 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001157
Klement Sekeraf62ae122016-10-11 11:47:09 +02001158 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1159 """
1160 Search the packet info list for the next packet info with same source
1161 and destination interface indexes
1162
1163 :param src_index: source interface index to search for
1164 :param dst_index: destination interface index to search for
1165 :param info: packet info - where to start the search
1166 :returns: packet info or None
1167
1168 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001169 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001170 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001171 if info is None:
1172 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001173 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001174 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001175
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001176 def assert_equal(self, real_value, expected_value, name_or_class=None):
1177 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001178 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001179 return
1180 try:
1181 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001182 msg = msg % (
1183 getdoc(name_or_class).strip(),
1184 real_value,
1185 str(name_or_class(real_value)),
1186 expected_value,
1187 str(name_or_class(expected_value)),
1188 )
Klement Sekera13a83ef2018-03-21 12:35:51 +01001189 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001190 msg = "Invalid %s: %s does not match expected value %s" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001191 name_or_class,
1192 real_value,
1193 expected_value,
1194 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001195
1196 self.assertEqual(real_value, expected_value, msg)
1197
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001198 def assert_in_range(self, real_value, expected_min, expected_max, name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001199 if name is None:
1200 msg = None
1201 else:
1202 msg = "Invalid %s: %s out of range <%s,%s>" % (
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001203 name,
1204 real_value,
1205 expected_min,
1206 expected_max,
1207 )
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001208 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1209
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001210 def assert_packet_checksums_valid(self, packet, ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001211 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001212 udp_layers = ["UDP", "UDPerror"]
1213 checksum_fields = ["cksum", "chksum"]
Klement Sekerad81ae412018-05-16 10:52:54 +02001214 checksums = []
1215 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001216 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001217 while True:
1218 layer = temp.getlayer(counter)
1219 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001220 layer = layer.copy()
1221 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001222 for cf in checksum_fields:
1223 if hasattr(layer, cf):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001224 if (
1225 ignore_zero_udp_checksums
1226 and 0 == getattr(layer, cf)
1227 and layer.name in udp_layers
1228 ):
Klement Sekerad81ae412018-05-16 10:52:54 +02001229 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001230 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001231 checksums.append((counter, cf))
1232 else:
1233 break
1234 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001235 if 0 == len(checksums):
1236 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001237 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001238 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001239 calc_sum = getattr(temp[layer], cf)
1240 self.assert_equal(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001241 getattr(received[layer], cf),
1242 calc_sum,
1243 "packet checksum on layer #%d: %s" % (layer, temp[layer].name),
1244 )
Klement Sekera31da2e32018-06-24 22:49:55 +02001245 self.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001246 "Checksum field `%s` on `%s` layer has correct value `%s`"
1247 % (cf, temp[layer].name, calc_sum)
1248 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001249
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001250 def assert_checksum_valid(
1251 self, received_packet, layer, field_name="chksum", ignore_zero_checksum=False
1252 ):
1253 """Check checksum of received packet on given layer"""
Klement Sekerad81ae412018-05-16 10:52:54 +02001254 received_packet_checksum = getattr(received_packet[layer], field_name)
1255 if ignore_zero_checksum and 0 == received_packet_checksum:
1256 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001257 recalculated = received_packet.__class__(scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001258 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001259 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001260 self.assert_equal(
1261 received_packet_checksum,
1262 getattr(recalculated[layer], field_name),
1263 "packet checksum on layer: %s" % layer,
1264 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001265
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001266 def assert_ip_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1267 self.assert_checksum_valid(
1268 received_packet, "IP", ignore_zero_checksum=ignore_zero_checksum
1269 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001270
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001271 def assert_tcp_checksum_valid(self, received_packet, ignore_zero_checksum=False):
1272 self.assert_checksum_valid(
1273 received_packet, "TCP", ignore_zero_checksum=ignore_zero_checksum
1274 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001275
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001276 def assert_udp_checksum_valid(self, received_packet, ignore_zero_checksum=True):
1277 self.assert_checksum_valid(
1278 received_packet, "UDP", ignore_zero_checksum=ignore_zero_checksum
1279 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001280
1281 def assert_embedded_icmp_checksum_valid(self, received_packet):
1282 if received_packet.haslayer(IPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001283 self.assert_checksum_valid(received_packet, "IPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001284 if received_packet.haslayer(TCPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001285 self.assert_checksum_valid(received_packet, "TCPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001286 if received_packet.haslayer(UDPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001287 self.assert_checksum_valid(
1288 received_packet, "UDPerror", ignore_zero_checksum=True
1289 )
Klement Sekerad81ae412018-05-16 10:52:54 +02001290 if received_packet.haslayer(ICMPerror):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001291 self.assert_checksum_valid(received_packet, "ICMPerror")
Klement Sekerad81ae412018-05-16 10:52:54 +02001292
1293 def assert_icmp_checksum_valid(self, received_packet):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001294 self.assert_checksum_valid(received_packet, "ICMP")
Klement Sekerad81ae412018-05-16 10:52:54 +02001295 self.assert_embedded_icmp_checksum_valid(received_packet)
1296
1297 def assert_icmpv6_checksum_valid(self, pkt):
1298 if pkt.haslayer(ICMPv6DestUnreach):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001299 self.assert_checksum_valid(pkt, "ICMPv6DestUnreach", "cksum")
Klement Sekerad81ae412018-05-16 10:52:54 +02001300 self.assert_embedded_icmp_checksum_valid(pkt)
1301 if pkt.haslayer(ICMPv6EchoRequest):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001302 self.assert_checksum_valid(pkt, "ICMPv6EchoRequest", "cksum")
Klement Sekerad81ae412018-05-16 10:52:54 +02001303 if pkt.haslayer(ICMPv6EchoReply):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001304 self.assert_checksum_valid(pkt, "ICMPv6EchoReply", "cksum")
Klement Sekerad81ae412018-05-16 10:52:54 +02001305
Klement Sekera107ad732022-02-18 10:32:08 +00001306 def get_counter(self, counter):
Klement Sekera3a343d42019-05-16 14:35:46 +02001307 if counter.startswith("/"):
1308 counter_value = self.statistics.get_counter(counter)
1309 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001310 counters = self.vapi.cli("sh errors").split("\n")
Klement Sekera6aa58b72019-05-16 14:34:55 +02001311 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001312 for i in range(1, len(counters) - 1):
1313 results = counters[i].split()
1314 if results[1] == counter:
1315 counter_value = int(results[0])
1316 break
1317 return counter_value
1318
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001319 def assert_counter_equal(self, counter, expected_value, thread=None, index=0):
Klement Sekera107ad732022-02-18 10:32:08 +00001320 c = self.get_counter(counter)
1321 if thread is not None:
1322 c = c[thread][index]
1323 else:
1324 c = sum(x[index] for x in c)
1325 self.assert_equal(c, expected_value, "counter `%s'" % counter)
1326
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001327 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera107ad732022-02-18 10:32:08 +00001328 counter_value = self.get_counter(counter)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001329 self.assert_equal(
1330 counter_value, expected_value, "packet counter `%s'" % counter
1331 )
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001332
Ole Troan233e4682019-05-16 15:01:34 +02001333 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001334 counter_value = self.statistics[counter].sum()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001335 self.assert_equal(counter_value, expected_value, "error counter `%s'" % counter)
Ole Troan233e4682019-05-16 15:01:34 +02001336
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001337 @classmethod
1338 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001339
1340 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1341 # * by Guido, only the main thread can be interrupted.
1342 # */
1343 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1344 if timeout == 0:
1345 # yield quantum
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001346 if hasattr(os, "sched_yield"):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001347 os.sched_yield()
1348 else:
1349 time.sleep(0)
1350 return
1351
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001352 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001353 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001354 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001355 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001356 if after - before > 2 * timeout:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001357 cls.logger.error(
1358 "unexpected self.sleep() result - slept for %es instead of ~%es!",
1359 after - before,
1360 timeout,
1361 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001362
1363 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001364 "Finished sleep (%s) - slept %es (wanted %es)",
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001365 remark,
1366 after - before,
1367 timeout,
1368 )
Klement Sekeraa57a9702017-02-02 06:58:07 +01001369
Benoît Ganne56eccdb2021-08-20 09:18:31 +02001370 def virtual_sleep(self, timeout, remark=None):
1371 self.logger.debug("Moving VPP time by %s (%s)", timeout, remark)
1372 self.vapi.cli("set clock adjust %s" % timeout)
1373
Benoît Ganne8c45e512021-02-19 16:39:13 +01001374 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001375 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001376 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001377 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001378
Klement Sekeraad3187f2022-02-18 10:34:35 +00001379 def snapshot_stats(self, stats_diff):
1380 """Return snapshot of interesting stats based on diff dictionary."""
1381 stats_snapshot = {}
1382 for sw_if_index in stats_diff:
1383 for counter in stats_diff[sw_if_index]:
1384 stats_snapshot[counter] = self.statistics[counter]
1385 self.logger.debug(f"Took statistics stats_snapshot: {stats_snapshot}")
1386 return stats_snapshot
1387
1388 def compare_stats_with_snapshot(self, stats_diff, stats_snapshot):
1389 """Assert appropriate difference between current stats and snapshot."""
1390 for sw_if_index in stats_diff:
1391 for cntr, diff in stats_diff[sw_if_index].items():
1392 if sw_if_index == "err":
1393 self.assert_equal(
1394 self.statistics[cntr].sum(),
1395 stats_snapshot[cntr].sum() + diff,
1396 f"'{cntr}' counter value (previous value: "
1397 f"{stats_snapshot[cntr].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001398 f"expected diff: {diff})",
1399 )
Klement Sekeraad3187f2022-02-18 10:34:35 +00001400 else:
1401 try:
1402 self.assert_equal(
1403 self.statistics[cntr][:, sw_if_index].sum(),
1404 stats_snapshot[cntr][:, sw_if_index].sum() + diff,
1405 f"'{cntr}' counter value (previous value: "
1406 f"{stats_snapshot[cntr][:, sw_if_index].sum()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001407 f"expected diff: {diff})",
1408 )
Klement Sekeraad3187f2022-02-18 10:34:35 +00001409 except IndexError:
1410 # if diff is 0, then this most probably a case where
1411 # test declares multiple interfaces but traffic hasn't
1412 # passed through this one yet - which means the counter
1413 # value is 0 and can be ignored
1414 if 0 != diff:
1415 raise
1416
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001417 def send_and_assert_no_replies(
1418 self, intf, pkts, remark="", timeout=None, stats_diff=None, trace=True, msg=None
1419 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001420 if stats_diff:
1421 stats_snapshot = self.snapshot_stats(stats_diff)
1422
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001423 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001424
1425 try:
1426 if not timeout:
1427 timeout = 1
1428 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +00001429 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001430 timeout = 0.1
1431 finally:
1432 if trace:
1433 if msg:
1434 self.logger.debug(f"send_and_assert_no_replies: {msg}")
1435 self.logger.debug(self.vapi.cli("show trace"))
1436
1437 if stats_diff:
1438 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -08001439
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001440 def send_and_expect(
1441 self,
1442 intf,
1443 pkts,
1444 output,
1445 n_rx=None,
1446 worker=None,
1447 trace=True,
1448 msg=None,
1449 stats_diff=None,
1450 ):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001451 if stats_diff:
1452 stats_snapshot = self.snapshot_stats(stats_diff)
1453
Neale Rannsd7603d92019-03-28 08:56:10 +00001454 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +00001455 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001456 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001457 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +02001458 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001459 if msg:
1460 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +02001461 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +00001462
1463 if stats_diff:
1464 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1465
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001466 return rx
1467
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001468 def send_and_expect_load_balancing(
1469 self, input, pkts, outputs, worker=None, trace=True
1470 ):
Neale Ranns699bea22022-02-17 09:22:16 +00001471 self.pg_send(input, pkts, worker=worker, trace=trace)
1472 rxs = []
1473 for oo in outputs:
1474 rx = oo._get_capture(1)
1475 self.assertNotEqual(0, len(rx))
1476 rxs.append(rx)
1477 if trace:
1478 self.logger.debug(self.vapi.cli("show trace"))
1479 return rxs
1480
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001481 def send_and_expect_some(self, intf, pkts, output, worker=None, trace=True):
Neale Ranns5c6dd172022-02-17 09:08:47 +00001482 self.pg_send(intf, pkts, worker=worker, trace=trace)
1483 rx = output._get_capture(1)
1484 if trace:
1485 self.logger.debug(self.vapi.cli("show trace"))
1486 self.assertTrue(len(rx) > 0)
1487 self.assertTrue(len(rx) < len(pkts))
1488 return rx
1489
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001490 def send_and_expect_only(self, intf, pkts, output, timeout=None, stats_diff=None):
Klement Sekeraad3187f2022-02-18 10:34:35 +00001491 if stats_diff:
1492 stats_snapshot = self.snapshot_stats(stats_diff)
1493
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001494 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001495 rx = output.get_capture(len(pkts))
1496 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001497 if not timeout:
1498 timeout = 1
1499 for i in self.pg_interfaces:
1500 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +00001501 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001502 timeout = 0.1
1503
Klement Sekeraad3187f2022-02-18 10:34:35 +00001504 if stats_diff:
1505 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1506
Neale Ranns52fae862018-01-08 04:41:42 -08001507 return rx
1508
Damjan Marionf56b77a2016-10-03 19:44:57 +02001509
juraj.linkes184870a2018-07-16 14:22:01 +02001510def get_testcase_doc_name(test):
1511 return getdoc(test.__class__).splitlines()[0]
1512
1513
Ole Trøan5ba91592018-11-22 10:01:09 +00001514def get_test_description(descriptions, test):
1515 short_description = test.shortDescription()
1516 if descriptions and short_description:
1517 return short_description
1518 else:
1519 return str(test)
1520
1521
juraj.linkes40dd73b2018-09-21 13:55:16 +02001522class TestCaseInfo(object):
1523 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1524 self.logger = logger
1525 self.tempdir = tempdir
1526 self.vpp_pid = vpp_pid
1527 self.vpp_bin_path = vpp_bin_path
1528 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001529
1530
Damjan Marionf56b77a2016-10-03 19:44:57 +02001531class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001532 """
1533 @property result_string
1534 String variable to store the test case result string.
1535 @property errors
1536 List variable containing 2-tuples of TestCase instances and strings
1537 holding formatted tracebacks. Each tuple represents a test which
1538 raised an unexpected exception.
1539 @property failures
1540 List variable containing 2-tuples of TestCase instances and strings
1541 holding formatted tracebacks. Each tuple represents a test where
1542 a failure was explicitly signalled using the TestCase.assert*()
1543 methods.
1544 """
1545
juraj.linkes40dd73b2018-09-21 13:55:16 +02001546 failed_test_cases_info = set()
1547 core_crash_test_cases_info = set()
1548 current_test_case_info = None
1549
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001550 def __init__(self, stream=None, descriptions=None, verbosity=None, runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001551 """
Klement Sekerada505f62017-01-04 12:58:53 +01001552 :param stream File descriptor to store where to report test results.
1553 Set to the standard error stream by default.
1554 :param descriptions Boolean variable to store information if to use
1555 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001556 :param verbosity Integer variable to store required verbosity level.
1557 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001558 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001559 self.stream = stream
1560 self.descriptions = descriptions
1561 self.verbosity = verbosity
1562 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001563 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001564 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001565
Damjan Marionf56b77a2016-10-03 19:44:57 +02001566 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001567 """
1568 Record a test succeeded result
1569
1570 :param test:
1571
1572 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001573 if self.current_test_case_info:
1574 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001575 "--- addSuccess() %s.%s(%s) called"
1576 % (test.__class__.__name__, test._testMethodName, test._testMethodDoc)
1577 )
Damjan Marionf56b77a2016-10-03 19:44:57 +02001578 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001579 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001580
juraj.linkescae64f82018-09-19 15:01:47 +02001581 self.send_result_through_pipe(test, PASS)
1582
Klement Sekeraf62ae122016-10-11 11:47:09 +02001583 def addSkip(self, test, reason):
1584 """
1585 Record a test skipped.
1586
1587 :param test:
1588 :param reason:
1589
1590 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001591 if self.current_test_case_info:
1592 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001593 "--- addSkip() %s.%s(%s) called, reason is %s"
1594 % (
1595 test.__class__.__name__,
1596 test._testMethodName,
1597 test._testMethodDoc,
1598 reason,
1599 )
1600 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001601 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001602 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001603
Klement Sekera558ceab2021-04-08 19:37:41 +02001604 if reason == "not enough cpus":
1605 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1606 else:
1607 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001608
juraj.linkes40dd73b2018-09-21 13:55:16 +02001609 def symlink_failed(self):
1610 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001611 try:
Klement Sekerab23ffd72021-05-31 16:08:53 +02001612 failed_dir = config.failed_dir
juraj.linkes40dd73b2018-09-21 13:55:16 +02001613 link_path = os.path.join(
1614 failed_dir,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001615 "%s-FAILED" % os.path.basename(self.current_test_case_info.tempdir),
1616 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001617
1618 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001619 "creating a link to the failed test"
1620 )
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001621 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001622 "os.symlink(%s, %s)"
1623 % (self.current_test_case_info.tempdir, link_path)
1624 )
juraj.linkes184870a2018-07-16 14:22:01 +02001625 if os.path.exists(link_path):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001626 self.current_test_case_info.logger.debug("symlink already exists")
juraj.linkes184870a2018-07-16 14:22:01 +02001627 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001628 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001629
Klement Sekeraf413bef2017-08-15 07:09:02 +02001630 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001631 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001632
juraj.linkescae64f82018-09-19 15:01:47 +02001633 def send_result_through_pipe(self, test, result):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001634 if hasattr(self, "test_framework_result_pipe"):
juraj.linkescae64f82018-09-19 15:01:47 +02001635 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001636 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001637 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001638
juraj.linkes40dd73b2018-09-21 13:55:16 +02001639 def log_error(self, test, err, fn_name):
1640 if self.current_test_case_info:
1641 if isinstance(test, unittest.suite._ErrorHolder):
1642 test_name = test.description
1643 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001644 test_name = "%s.%s(%s)" % (
1645 test.__class__.__name__,
1646 test._testMethodName,
1647 test._testMethodDoc,
1648 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001649 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001650 "--- %s() %s called, err is %s" % (fn_name, test_name, err)
1651 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001652 self.current_test_case_info.logger.debug(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001653 "formatted exception is:\n%s" % "".join(format_exception(*err))
1654 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001655
1656 def add_error(self, test, err, unittest_fn, error_type):
1657 if error_type == FAIL:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001658 self.log_error(test, err, "addFailure")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001659 error_type_str = colorize("FAIL", RED)
1660 elif error_type == ERROR:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001661 self.log_error(test, err, "addError")
juraj.linkes40dd73b2018-09-21 13:55:16 +02001662 error_type_str = colorize("ERROR", RED)
1663 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001664 raise Exception(
1665 "Error type %s cannot be used to record an "
1666 "error or a failure" % error_type
1667 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001668
1669 unittest_fn(self, test, err)
1670 if self.current_test_case_info:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001671 self.result_string = "%s [ temp dir used by test case: %s ]" % (
1672 error_type_str,
1673 self.current_test_case_info.tempdir,
1674 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001675 self.symlink_failed()
1676 self.failed_test_cases_info.add(self.current_test_case_info)
1677 if is_core_present(self.current_test_case_info.tempdir):
1678 if not self.current_test_case_info.core_crash_test:
1679 if isinstance(test, unittest.suite._ErrorHolder):
1680 test_name = str(test)
1681 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001682 test_name = "'{!s}' ({!s})".format(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001683 get_testcase_doc_name(test), test.id()
1684 )
juraj.linkes40dd73b2018-09-21 13:55:16 +02001685 self.current_test_case_info.core_crash_test = test_name
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001686 self.core_crash_test_cases_info.add(self.current_test_case_info)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001687 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001688 self.result_string = "%s [no temp dir]" % error_type_str
juraj.linkes40dd73b2018-09-21 13:55:16 +02001689
1690 self.send_result_through_pipe(test, error_type)
1691
Damjan Marionf56b77a2016-10-03 19:44:57 +02001692 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001693 """
1694 Record a test failed result
1695
1696 :param test:
1697 :param err: error message
1698
1699 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001700 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001701
Damjan Marionf56b77a2016-10-03 19:44:57 +02001702 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001703 """
1704 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001705
Klement Sekeraf62ae122016-10-11 11:47:09 +02001706 :param test:
1707 :param err: error message
1708
1709 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001710 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001711
Damjan Marionf56b77a2016-10-03 19:44:57 +02001712 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001713 """
1714 Get test description
1715
1716 :param test:
1717 :returns: test description
1718
1719 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001720 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001721
Damjan Marionf56b77a2016-10-03 19:44:57 +02001722 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001723 """
1724 Start a test
1725
1726 :param test:
1727
1728 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001729
1730 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001731 if test.__class__ in self.printed:
1732 return
1733
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001734 test_doc = getdoc(test)
1735 if not test_doc:
1736 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001737
Klement Sekeraea6236b2021-03-25 14:03:44 +01001738 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001739 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001740 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001741 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001742
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001743 # This block may overwrite the colorized title above,
1744 # but we want this to stand out and be fixed
1745 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001746 test_title = colorize(f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001747
Naveen Joy6eaeea92021-09-09 17:57:02 -07001748 if test.has_tag(TestCaseTag.FIXME_ASAN):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001749 test_title = colorize(f"FIXME with ASAN: {test_title}", RED)
Naveen Joy6eaeea92021-09-09 17:57:02 -07001750 test.skip_fixme_asan()
1751
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001752 if hasattr(test, "vpp_worker_count"):
Klement Sekeraea6236b2021-03-25 14:03:44 +01001753 if test.vpp_worker_count == 0:
1754 test_title += " [main thread only]"
1755 elif test.vpp_worker_count == 1:
1756 test_title += " [1 worker thread]"
1757 else:
1758 test_title += f" [{test.vpp_worker_count} worker threads]"
1759
Klement Sekera558ceab2021-04-08 19:37:41 +02001760 if test.__class__.skipped_due_to_cpu_lack:
1761 test_title = colorize(
1762 f"{test_title} [skipped - not enough cpus, "
1763 f"required={test.__class__.get_cpus_required()}, "
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001764 f"available={max_vpp_cpus}]",
1765 YELLOW,
1766 )
Klement Sekera558ceab2021-04-08 19:37:41 +02001767
1768 print(double_line_delim)
1769 print(test_title)
1770 print(double_line_delim)
1771 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001772
1773 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001774 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001775 unittest.TestResult.startTest(self, test)
1776 if self.verbosity > 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001777 self.stream.writeln("Starting " + self.getDescription(test) + " ...")
Klement Sekeraf62ae122016-10-11 11:47:09 +02001778 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001779
Damjan Marionf56b77a2016-10-03 19:44:57 +02001780 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001781 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001782 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001783
1784 :param test:
1785
1786 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001787 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001788
Damjan Marionf56b77a2016-10-03 19:44:57 +02001789 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001790 self.stream.writeln(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001791 self.stream.writeln(
1792 "%-73s%s" % (self.getDescription(test), self.result_string)
1793 )
Klement Sekeraf62ae122016-10-11 11:47:09 +02001794 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001795 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001796 self.stream.writeln(
1797 "%-68s %4.2f %s"
1798 % (
1799 self.getDescription(test),
1800 time.time() - self.start_test,
1801 self.result_string,
1802 )
1803 )
juraj.linkescae64f82018-09-19 15:01:47 +02001804
1805 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001806
Damjan Marionf56b77a2016-10-03 19:44:57 +02001807 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001808 """
1809 Print errors from running the test case
1810 """
juraj.linkesabec0122018-11-16 17:28:56 +01001811 if len(self.errors) > 0 or len(self.failures) > 0:
1812 self.stream.writeln()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001813 self.printErrorList("ERROR", self.errors)
1814 self.printErrorList("FAIL", self.failures)
juraj.linkesabec0122018-11-16 17:28:56 +01001815
1816 # ^^ that is the last output from unittest before summary
1817 if not self.runner.print_summary:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001818 devnull = unittest.runner._WritelnDecorator(open(os.devnull, "w"))
juraj.linkesabec0122018-11-16 17:28:56 +01001819 self.stream = devnull
1820 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001821
Damjan Marionf56b77a2016-10-03 19:44:57 +02001822 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001823 """
1824 Print error list to the output stream together with error type
1825 and test case description.
1826
1827 :param flavour: error type
1828 :param errors: iterable errors
1829
1830 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001831 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001832 self.stream.writeln(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001833 self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001834 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001835 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001836
1837
Damjan Marionf56b77a2016-10-03 19:44:57 +02001838class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001839 """
Klement Sekera104543f2017-02-03 07:29:43 +01001840 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001841 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001842
Klement Sekeraf62ae122016-10-11 11:47:09 +02001843 @property
1844 def resultclass(self):
1845 """Class maintaining the results of the tests"""
1846 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001847
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001848 def __init__(
1849 self,
1850 keep_alive_pipe=None,
1851 descriptions=True,
1852 verbosity=1,
1853 result_pipe=None,
1854 failfast=False,
1855 buffer=False,
1856 resultclass=None,
1857 print_summary=True,
1858 **kwargs,
1859 ):
Klement Sekera7a161da2017-01-17 13:42:48 +01001860 # ignore stream setting here, use hard-coded stdout to be in sync
1861 # with prints from VppTestCase methods ...
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001862 super(VppTestRunner, self).__init__(
1863 sys.stdout, descriptions, verbosity, failfast, buffer, resultclass, **kwargs
1864 )
juraj.linkesccfead62018-11-21 13:20:43 +01001865 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001866
juraj.linkesabec0122018-11-16 17:28:56 +01001867 self.orig_stream = self.stream
1868 self.resultclass.test_framework_result_pipe = result_pipe
1869
1870 self.print_summary = print_summary
1871
1872 def _makeResult(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001873 return self.resultclass(self.stream, self.descriptions, self.verbosity, self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001874
Damjan Marionf56b77a2016-10-03 19:44:57 +02001875 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001876 """
1877 Run the tests
1878
1879 :param test:
1880
1881 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001882 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001883
1884 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001885 if not self.print_summary:
1886 self.stream = self.orig_stream
1887 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001888 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001889
1890
1891class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001892 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1893 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001894 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001895 self.args = executable_args
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001896 if hasattr(self, "testcase") and self.testcase.debug_all:
Dave Wallace24564332019-10-21 02:53:14 +00001897 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001898 self.args = [
1899 "/usr/bin/gdbserver",
1900 "localhost:{port}".format(port=self.testcase.gdbserver_port),
1901 ] + args
1902 elif self.testcase.debug_gdb and hasattr(self, "wait_for_gdb"):
Dave Wallace24564332019-10-21 02:53:14 +00001903 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001904 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001905 self.app_name = os.path.basename(self.app_bin)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001906 if hasattr(self, "role"):
1907 self.app_name += " {role}".format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001908 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001909 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001910 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001911 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001912
Dave Wallace24564332019-10-21 02:53:14 +00001913 def wait_for_enter(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001914 if not hasattr(self, "testcase"):
Dave Wallace24564332019-10-21 02:53:14 +00001915 return
1916 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1917 print()
1918 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001919 print(
1920 "Spawned GDB Server for '{app}' with PID: {pid}".format(
1921 app=self.app_name, pid=self.process.pid
1922 )
1923 )
Dave Wallace24564332019-10-21 02:53:14 +00001924 elif self.testcase.debug_all and self.testcase.debug_gdb:
1925 print()
1926 print(double_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001927 print(
1928 "Spawned '{app}' with PID: {pid}".format(
1929 app=self.app_name, pid=self.process.pid
1930 )
1931 )
Dave Wallace24564332019-10-21 02:53:14 +00001932 else:
1933 return
1934 print(single_line_delim)
1935 print("You can debug '{app}' using:".format(app=self.app_name))
1936 if self.testcase.debug_gdbserver:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001937 print(
1938 "sudo gdb "
1939 + self.app_bin
1940 + " -ex 'target remote localhost:{port}'".format(
1941 port=self.testcase.gdbserver_port
1942 )
1943 )
1944 print(
1945 "Now is the time to attach gdb by running the above "
1946 "command, set up breakpoints etc., then resume from "
1947 "within gdb by issuing the 'continue' command"
1948 )
Dave Wallace24564332019-10-21 02:53:14 +00001949 self.testcase.gdbserver_port += 1
1950 elif self.testcase.debug_gdb:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001951 print(
1952 "sudo gdb "
1953 + self.app_bin
1954 + " -ex 'attach {pid}'".format(pid=self.process.pid)
1955 )
1956 print(
1957 "Now is the time to attach gdb by running the above "
1958 "command and set up breakpoints etc., then resume from"
1959 " within gdb by issuing the 'continue' command"
1960 )
Dave Wallace24564332019-10-21 02:53:14 +00001961 print(single_line_delim)
1962 input("Press ENTER to continue running the testcase...")
1963
Neale Ranns812ed392017-10-16 04:20:13 -07001964 def run(self):
1965 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001966 if not os.path.exists(executable) or not os.access(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001967 executable, os.F_OK | os.X_OK
1968 ):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001969 # Exit code that means some system file did not exist,
1970 # could not be opened, or had some other kind of error.
1971 self.result = os.EX_OSFILE
1972 raise EnvironmentError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001973 "executable '%s' is not found or executable." % executable
1974 )
1975 self.logger.debug(
1976 "Running executable '{app}': '{cmd}'".format(
1977 app=self.app_name, cmd=" ".join(self.args)
1978 )
1979 )
Neale Ranns812ed392017-10-16 04:20:13 -07001980 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001981 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001982 env["CK_LOG_FILE_NAME"] = "-"
1983 self.process = subprocess.Popen(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001984 ["stdbuf", "-o0", "-e0"] + self.args,
1985 shell=False,
1986 env=env,
1987 preexec_fn=os.setpgrp,
1988 stdout=subprocess.PIPE,
1989 stderr=subprocess.PIPE,
1990 )
Dave Wallace24564332019-10-21 02:53:14 +00001991 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001992 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001993 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001994 self.logger.info("Return code is `%s'" % self.process.returncode)
1995 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001996 self.logger.info(
1997 "Executable `{app}' wrote to stdout:".format(app=self.app_name)
1998 )
Neale Ranns812ed392017-10-16 04:20:13 -07001999 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002000 self.logger.info(out.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002001 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002002 self.logger.info(
2003 "Executable `{app}' wrote to stderr:".format(app=self.app_name)
2004 )
Neale Ranns812ed392017-10-16 04:20:13 -07002005 self.logger.info(single_line_delim)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002006 self.logger.info(err.decode("utf-8"))
Neale Ranns812ed392017-10-16 04:20:13 -07002007 self.logger.info(single_line_delim)
2008 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002009
Klement Sekera6aa58b72019-05-16 14:34:55 +02002010
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002011if __name__ == "__main__":
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08002012 pass