blob: 8065518ff7a3cfe2bea102f47e5909e2bcc8bdb5 [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
Ole Trøan162989e2018-11-26 10:27:50 +000040from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
41 get_logger, colorize
42from vpp_object import VppObjectRegistry
43from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020044from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
45from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
46from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080047
Klement Sekera558ceab2021-04-08 19:37:41 +020048
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050049logger = logging.getLogger(__name__)
50
51# Set up an empty logger for the testcase that can be overridden as necessary
52null_logger = logging.getLogger('VppTestCase')
53null_logger.addHandler(logging.NullHandler())
54
juraj.linkescae64f82018-09-19 15:01:47 +020055PASS = 0
56FAIL = 1
57ERROR = 2
58SKIP = 3
59TEST_RUN = 4
Klement Sekera558ceab2021-04-08 19:37:41 +020060SKIP_CPU_SHORTAGE = 5
juraj.linkescae64f82018-09-19 15:01:47 +020061
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040062
Klement Sekerab23ffd72021-05-31 16:08:53 +020063if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010064 import debug_internal
65
Klement Sekeraf62ae122016-10-11 11:47:09 +020066"""
67 Test framework module.
68
69 The module provides a set of tools for constructing and running tests and
70 representing the results.
71"""
72
Klement Sekeraf62ae122016-10-11 11:47:09 +020073
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040074class VppDiedError(Exception):
75 """ exception for reporting that the subprocess has died."""
76
77 signals_by_value = {v: k for k, v in signal.__dict__.items() if
78 k.startswith('SIG') and not k.startswith('SIG_')}
79
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040080 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040081 self.rv = rv
82 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040083 self.testcase = testcase
84 self.method_name = method_name
85
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040086 try:
87 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -040088 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040089 pass
90
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040091 if testcase is None and method_name is None:
92 in_msg = ''
93 else:
Klement Sekera79a31db2021-03-12 18:16:10 +010094 in_msg = ' while running %s.%s' % (testcase, method_name)
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040095
Klement Sekera79a31db2021-03-12 18:16:10 +010096 if self.rv:
97 msg = "VPP subprocess died unexpectedly%s with return code: %d%s."\
98 % (in_msg, self.rv, ' [%s]' %
99 (self.signal_name if
100 self.signal_name is not None else ''))
101 else:
102 msg = "VPP subprocess died unexpectedly%s." % in_msg
103
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400104 super(VppDiedError, self).__init__(msg)
105
106
Damjan Marionf56b77a2016-10-03 19:44:57 +0200107class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200108 """Private class to create packet info object.
109
110 Help process information about the next packet.
111 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200112 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100113 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200114 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100115 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200116 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100117 #: Store the index of the destination packet generator interface
118 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200119 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100120 #: Store expected ip version
121 ip = -1
122 #: Store expected upper protocol
123 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100124 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200125 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200126
Matej Klotton16a14cd2016-12-07 15:09:13 +0100127 def __eq__(self, other):
128 index = self.index == other.index
129 src = self.src == other.src
130 dst = self.dst == other.dst
131 data = self.data == other.data
132 return index and src and dst and data
133
Klement Sekeraf62ae122016-10-11 11:47:09 +0200134
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100135def pump_output(testclass):
136 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100137 stdout_fragment = ""
138 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400139 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100140 readable = select.select([testclass.vpp.stdout.fileno(),
141 testclass.vpp.stderr.fileno(),
142 testclass.pump_thread_wakeup_pipe[0]],
143 [], [])[0]
144 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100145 read = os.read(testclass.vpp.stdout.fileno(), 102400)
146 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200147 split = read.decode('ascii',
148 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100149 if len(stdout_fragment) > 0:
150 split[0] = "%s%s" % (stdout_fragment, split[0])
151 if len(split) > 0 and split[-1].endswith("\n"):
152 limit = None
153 else:
154 limit = -1
155 stdout_fragment = split[-1]
156 testclass.vpp_stdout_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200157 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100158 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100159 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100160 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100161 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100162 read = os.read(testclass.vpp.stderr.fileno(), 102400)
163 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200164 split = read.decode('ascii',
165 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100166 if len(stderr_fragment) > 0:
167 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200168 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100169 limit = None
170 else:
171 limit = -1
172 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200173
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100174 testclass.vpp_stderr_deque.extend(split[:limit])
Klement Sekerab23ffd72021-05-31 16:08:53 +0200175 if not config.cache_vpp_output:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100176 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100177 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100178 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800179 # ignoring the dummy pipe here intentionally - the
180 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200181
182
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800183def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100184 return platform.machine() == 'aarch64'
185
Klement Sekera6aa58b72019-05-16 14:34:55 +0200186
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800187is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100188
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800189
Klement Sekera909a6a12017-08-08 04:33:53 +0200190class KeepAliveReporter(object):
191 """
192 Singleton object which reports test start to parent process
193 """
194 _shared_state = {}
195
196 def __init__(self):
197 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800198 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200199
200 @property
201 def pipe(self):
202 return self._pipe
203
204 @pipe.setter
205 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800206 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200207 raise Exception("Internal error - pipe should only be set once.")
208 self._pipe = pipe
209
juraj.linkes40dd73b2018-09-21 13:55:16 +0200210 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200211 """
212 Write current test tmpdir & desc to keep-alive pipe to signal liveness
213 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200214 if self.pipe is None:
215 # if not running forked..
216 return
217
Klement Sekera909a6a12017-08-08 04:33:53 +0200218 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200219 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200220 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200221 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200222
Klement Sekerab23ffd72021-05-31 16:08:53 +0200223 self.pipe.send((desc, config.vpp, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200224
225
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000226class TestCaseTag(Enum):
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000227 # marks the suites that must run at the end
228 # using only a single test runner
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000229 RUN_SOLO = 1
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000230 # marks the suites broken on VPP multi-worker
231 FIXME_VPP_WORKERS = 2
Naveen Joy6eaeea92021-09-09 17:57:02 -0700232 # marks the suites broken when ASan is enabled
233 FIXME_ASAN = 3
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000234
235
236def create_tag_decorator(e):
237 def decorator(cls):
238 try:
239 cls.test_tags.append(e)
240 except AttributeError:
241 cls.test_tags = [e]
242 return cls
243 return decorator
244
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000245
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000246tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +0000247tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
Naveen Joy6eaeea92021-09-09 17:57:02 -0700248tag_fixme_asan = create_tag_decorator(TestCaseTag.FIXME_ASAN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000249
250
Klement Sekerae2636852021-03-16 12:52:12 +0100251class DummyVpp:
252 returncode = None
253 pid = 0xcafebafe
254
255 def poll(self):
256 pass
257
258 def terminate(self):
259 pass
260
261
Klement Sekera558ceab2021-04-08 19:37:41 +0200262class CPUInterface(ABC):
263 cpus = []
264 skipped_due_to_cpu_lack = False
265
266 @classmethod
267 @abstractmethod
268 def get_cpus_required(cls):
269 pass
270
271 @classmethod
272 def assign_cpus(cls, cpus):
273 cls.cpus = cpus
274
275
276class VppTestCase(CPUInterface, unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100277 """This subclass is a base class for VPP test cases that are implemented as
278 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200279 """
280
Arthur de Kerhordb023802021-03-11 10:26:54 -0800281 extra_vpp_statseg_config = ""
Ole Troana45dc072018-12-21 16:04:22 +0100282 extra_vpp_punt_config = []
283 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500284 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400285 vapi_response_timeout = 5
Klement Sekera140af152022-02-18 10:30:51 +0000286 remove_configured_vpp_objects_on_tear_down = True
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100287
Klement Sekeraf62ae122016-10-11 11:47:09 +0200288 @property
289 def packet_infos(self):
290 """List of packet infos"""
291 return self._packet_infos
292
Klement Sekeradab231a2016-12-21 08:50:14 +0100293 @classmethod
294 def get_packet_count_for_if_idx(cls, dst_if_index):
295 """Get the number of packet info for specified destination if index"""
296 if dst_if_index in cls._packet_count_for_dst_if_idx:
297 return cls._packet_count_for_dst_if_idx[dst_if_index]
298 else:
299 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200300
301 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000302 def has_tag(cls, tag):
303 """ if the test case has a given tag - return true """
304 try:
305 return tag in cls.test_tags
306 except AttributeError:
307 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000308 return False
309
310 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000311 def is_tagged_run_solo(cls):
312 """ if the test case class is timing-sensitive - return true """
313 return cls.has_tag(TestCaseTag.RUN_SOLO)
314
315 @classmethod
Naveen Joy6eaeea92021-09-09 17:57:02 -0700316 def skip_fixme_asan(cls):
317 """ if @tag_fixme_asan & ASan is enabled - mark for skip """
318 if cls.has_tag(TestCaseTag.FIXME_ASAN):
319 vpp_extra_cmake_args = os.environ.get('VPP_EXTRA_CMAKE_ARGS', '')
320 if 'DVPP_ENABLE_SANITIZE_ADDR=ON' in vpp_extra_cmake_args:
321 cls = unittest.skip("Skipping @tag_fixme_asan tests")(cls)
322
323 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200324 def instance(cls):
325 """Return the instance of this testcase"""
326 return cls.test_instance
327
Damjan Marionf56b77a2016-10-03 19:44:57 +0200328 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200329 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000330 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200331 cls.debug_core = False
332 cls.debug_gdb = False
333 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000334 cls.debug_all = False
Klement Sekerae2636852021-03-16 12:52:12 +0100335 cls.debug_attach = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200336 if d is None:
337 return
338 dl = d.lower()
339 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200340 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000341 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200342 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000343 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200344 cls.debug_gdbserver = True
Klement Sekerae2636852021-03-16 12:52:12 +0100345 elif dl == "attach":
346 cls.debug_attach = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200347 else:
348 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000349 if dl == "gdb-all" or dl == "gdbserver-all":
350 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200351
Klement Sekera558ceab2021-04-08 19:37:41 +0200352 @classmethod
353 def get_vpp_worker_count(cls):
354 if not hasattr(cls, "vpp_worker_count"):
355 if cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
356 cls.vpp_worker_count = 0
357 else:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200358 cls.vpp_worker_count = config.vpp_worker_count
Klement Sekera558ceab2021-04-08 19:37:41 +0200359 return cls.vpp_worker_count
juraj.linkes184870a2018-07-16 14:22:01 +0200360
Klement Sekera558ceab2021-04-08 19:37:41 +0200361 @classmethod
362 def get_cpus_required(cls):
363 return 1 + cls.get_vpp_worker_count()
juraj.linkes184870a2018-07-16 14:22:01 +0200364
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800365 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200366 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200367 """ Set-up the test case class based on environment variables """
Klement Sekerab23ffd72021-05-31 16:08:53 +0200368 cls.step = config.step
369 cls.plugin_path = ":".join(config.vpp_plugin_dir)
370 cls.test_plugin_path = ":".join(config.vpp_test_plugin_dir)
371 cls.extern_plugin_path = ":".join(config.extern_plugin_dir)
Ole Troana45dc072018-12-21 16:04:22 +0100372 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100373 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100374 debug_cli = "cli-listen localhost:5002"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200375 size = re.search(r"\d+[gG]", config.coredump_size)
376 if size:
377 coredump_size = f"coredump-size {config.coredump_size}".lower()
378 else:
Ole Troana45dc072018-12-21 16:04:22 +0100379 coredump_size = "coredump-size unlimited"
Klement Sekerab23ffd72021-05-31 16:08:53 +0200380 default_variant = config.variant
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000381 if default_variant is not None:
382 default_variant = "defaults { %s 100 }" % default_variant
383 else:
384 default_variant = ""
385
Klement Sekerab23ffd72021-05-31 16:08:53 +0200386 api_fuzzing = config.api_fuzz
Dave Barach77841402020-04-29 17:04:10 -0400387 if api_fuzzing is None:
388 api_fuzzing = 'off'
389
Klement Sekera8d815022021-03-15 16:58:10 +0100390 cls.vpp_cmdline = [
Klement Sekerab23ffd72021-05-31 16:08:53 +0200391 config.vpp,
Klement Sekera8d815022021-03-15 16:58:10 +0100392 "unix", "{", "nodaemon", debug_cli, "full-coredump",
393 coredump_size, "runtime-dir", cls.tempdir, "}",
394 "api-trace", "{", "on", "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100395 "api-segment", "{", "prefix", cls.get_api_segment_prefix(), "}",
Klement Sekera558ceab2021-04-08 19:37:41 +0200396 "cpu", "{", "main-core", str(cls.cpus[0]), ]
Klement Sekerab23ffd72021-05-31 16:08:53 +0200397 if cls.extern_plugin_path not in (None, ""):
Damjan Marion5546e432021-09-30 20:04:14 +0200398 cls.extra_vpp_plugin_config.append(
Klement Sekerab23ffd72021-05-31 16:08:53 +0200399 "add-path %s" % cls.extern_plugin_path)
Klement Sekera558ceab2021-04-08 19:37:41 +0200400 if cls.get_vpp_worker_count():
401 cls.vpp_cmdline.extend([
402 "corelist-workers", ",".join([str(x) for x in cls.cpus[1:]])])
Klement Sekera8d815022021-03-15 16:58:10 +0100403 cls.vpp_cmdline.extend([
404 "}",
405 "physmem", "{", "max-size", "32m", "}",
Arthur de Kerhordb023802021-03-11 10:26:54 -0800406 "statseg", "{", "socket-name", cls.get_stats_sock_path(),
407 cls.extra_vpp_statseg_config, "}",
Klement Sekerae2636852021-03-16 12:52:12 +0100408 "socksvr", "{", "socket-name", cls.get_api_sock_path(), "}",
Klement Sekera8d815022021-03-15 16:58:10 +0100409 "node { ", default_variant, "}",
410 "api-fuzz {", api_fuzzing, "}",
411 "plugins", "{", "plugin", "dpdk_plugin.so", "{", "disable", "}",
412 "plugin", "rdma_plugin.so", "{", "disable", "}",
413 "plugin", "lisp_unittest_plugin.so", "{", "enable", "}",
414 "plugin", "unittest_plugin.so", "{", "enable", "}"
415 ] + cls.extra_vpp_plugin_config + ["}", ])
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000416
Ole Troana45dc072018-12-21 16:04:22 +0100417 if cls.extra_vpp_punt_config is not None:
418 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Dave Barach7d31ab22019-05-08 19:18:18 -0400419
Klement Sekerae2636852021-03-16 12:52:12 +0100420 if not cls.debug_attach:
421 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
422 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200423
424 @classmethod
425 def wait_for_enter(cls):
426 if cls.debug_gdbserver:
427 print(double_line_delim)
428 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
429 elif cls.debug_gdb:
430 print(double_line_delim)
431 print("Spawned VPP with PID: %d" % cls.vpp.pid)
432 else:
433 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
434 return
435 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000436 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200437 if cls.debug_gdbserver:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200438 print(f"sudo gdb {config.vpp} "
439 f"-ex 'target remote localhost:{cls.gdbserver_port}'")
Dave Wallace24564332019-10-21 02:53:14 +0000440 print("Now is the time to attach gdb by running the above "
441 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200442 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000443 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200444 elif cls.debug_gdb:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200445 print(f"sudo gdb {config.vpp} -ex 'attach {cls.vpp.pid}'")
Dave Wallace24564332019-10-21 02:53:14 +0000446 print("Now is the time to attach gdb by running the above "
447 "command and set up breakpoints etc., then resume VPP from"
448 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200449 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800450 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200451
452 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100453 def attach_vpp(cls):
454 cls.vpp = DummyVpp()
455
456 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200457 def run_vpp(cls):
Klement Sekera558ceab2021-04-08 19:37:41 +0200458 cls.logger.debug(f"Assigned cpus: {cls.cpus}")
Klement Sekera277b89c2016-10-28 13:20:27 +0200459 cmdline = cls.vpp_cmdline
460
461 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100462 gdbserver = '/usr/bin/gdbserver'
Klement Sekera558ceab2021-04-08 19:37:41 +0200463 if not os.path.isfile(gdbserver) or\
Klement Sekera931be3a2016-11-03 05:36:01 +0100464 not os.access(gdbserver, os.X_OK):
465 raise Exception("gdbserver binary '%s' does not exist or is "
466 "not executable" % gdbserver)
467
Dave Wallace24564332019-10-21 02:53:14 +0000468 cmdline = [gdbserver, 'localhost:{port}'
469 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200470 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
471
Klement Sekera931be3a2016-11-03 05:36:01 +0100472 try:
473 cls.vpp = subprocess.Popen(cmdline,
474 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100475 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800476 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800477 cls.logger.critical("Subprocess returned with non-0 return code: ("
478 "%s)", e.returncode)
479 raise
480 except OSError as e:
481 cls.logger.critical("Subprocess returned with OS error: "
482 "(%s) %s", e.errno, e.strerror)
483 raise
484 except Exception as e:
485 cls.logger.exception("Subprocess returned unexpected from "
486 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100487 raise
488
Klement Sekera277b89c2016-10-28 13:20:27 +0200489 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100490
Damjan Marionf56b77a2016-10-03 19:44:57 +0200491 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000492 def wait_for_coredump(cls):
493 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400494 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000495 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400496 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000497 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400498 ok = False
499 while time.time() < deadline:
500 cls.sleep(1)
501 size = curr_size
502 curr_size = os.path.getsize(corefile)
503 if size == curr_size:
504 ok = True
505 break
506 if not ok:
507 cls.logger.error("Timed out waiting for coredump to complete:"
508 " %s", corefile)
509 else:
510 cls.logger.error("Coredump complete: %s, size %d",
511 corefile, curr_size)
512
513 @classmethod
Klement Sekerae2636852021-03-16 12:52:12 +0100514 def get_stats_sock_path(cls):
515 return "%s/stats.sock" % cls.tempdir
516
517 @classmethod
518 def get_api_sock_path(cls):
519 return "%s/api.sock" % cls.tempdir
520
521 @classmethod
522 def get_api_segment_prefix(cls):
523 return os.path.basename(cls.tempdir) # Only used for VAPI
524
525 @classmethod
526 def get_tempdir(cls):
Klement Sekerab3fc6582022-03-10 11:47:45 +0100527 if cls.debug_attach:
528 tmpdir = f"{config.tmp_dir}/unittest-attach-gdb"
529 else:
530 tmpdir = f"{config.tmp_dir}/vpp-unittest-{cls.__name__}"
531 if config.wipe_tmp_dir:
532 shutil.rmtree(tmpdir, ignore_errors=True)
533 os.mkdir(tmpdir)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200534 return tmpdir
535
536 @classmethod
537 def create_file_handler(cls):
538 if config.log_dir is None:
539 cls.file_handler = FileHandler(f"{cls.tempdir}/log.txt")
540 return
541
542 logdir = f"{config.log_dir}/vpp-unittest-{cls.__name__}"
543 if config.wipe_tmp_dir:
544 shutil.rmtree(logdir, ignore_errors=True)
545 os.mkdir(logdir)
546 cls.file_handler = FileHandler(f"{logdir}/log.txt")
Klement Sekerae2636852021-03-16 12:52:12 +0100547
548 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200549 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200550 """
551 Perform class setup before running the testcase
552 Remove shared memory files, start vpp and connect the vpp-api
553 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800554 super(VppTestCase, cls).setUpClass()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100555 cls.logger = get_logger(cls.__name__)
Klement Sekerab23ffd72021-05-31 16:08:53 +0200556 random.seed(config.rnd_seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100557 if hasattr(cls, 'parallel_handler'):
558 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100559 cls.logger.propagate = False
Klement Sekerab23ffd72021-05-31 16:08:53 +0200560 cls.set_debug_flags(config.debug)
Klement Sekerae2636852021-03-16 12:52:12 +0100561 cls.tempdir = cls.get_tempdir()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200562 cls.create_file_handler()
Klement Sekera027dbd52017-04-11 06:01:53 +0200563 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100564 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
565 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200566 cls.file_handler.setLevel(DEBUG)
567 cls.logger.addHandler(cls.file_handler)
Klement Sekerae2636852021-03-16 12:52:12 +0100568 cls.logger.debug("--- setUpClass() for %s called ---" % cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200569 os.chdir(cls.tempdir)
Ole Troan4376ab22021-03-03 10:40:05 +0100570 cls.logger.info("Temporary dir is %s, api socket is %s",
Klement Sekerae2636852021-03-16 12:52:12 +0100571 cls.tempdir, cls.get_api_sock_path())
Klement Sekerab23ffd72021-05-31 16:08:53 +0200572 cls.logger.debug("Random seed is %s", config.rnd_seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200573 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100574 cls.reset_packet_infos()
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200575 cls._pcaps = []
576 cls._old_pcaps = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200577 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100578 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100579 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200580 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200581 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200582 # need to catch exceptions here because if we raise, then the cleanup
583 # doesn't get called and we might end with a zombie vpp
584 try:
Klement Sekerae2636852021-03-16 12:52:12 +0100585 if cls.debug_attach:
586 cls.attach_vpp()
587 else:
588 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200589 cls.reporter.send_keep_alive(cls, 'setUpClass')
590 VppTestResult.current_test_case_info = TestCaseInfo(
Klement Sekerab23ffd72021-05-31 16:08:53 +0200591 cls.logger, cls.tempdir, cls.vpp.pid, config.vpp)
Klement Sekerae4504c62016-12-08 10:16:41 +0100592 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100593 cls.vpp_stderr_deque = deque()
Klement Sekerae2636852021-03-16 12:52:12 +0100594 if not cls.debug_attach:
595 cls.pump_thread_stop_flag = Event()
596 cls.pump_thread_wakeup_pipe = os.pipe()
597 cls.pump_thread = Thread(target=pump_output, args=(cls,))
598 cls.pump_thread.daemon = True
599 cls.pump_thread.start()
600 if cls.debug_gdb or cls.debug_gdbserver or cls.debug_attach:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400601 cls.vapi_response_timeout = 0
Ole Troan4376ab22021-03-03 10:40:05 +0100602 cls.vapi = VppPapiProvider(cls.__name__, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400603 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100604 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400605 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100606 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400607 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100608 cls.vapi.register_hook(hook)
Klement Sekerae2636852021-03-16 12:52:12 +0100609 cls.statistics = VPPStats(socketname=cls.get_stats_sock_path())
Klement Sekera3747c752017-04-10 06:30:17 +0200610 try:
611 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100612 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200613 cls.vpp_startup_failed = True
614 cls.logger.critical(
615 "VPP died shortly after startup, check the"
616 " output to standard error for possible cause")
617 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100618 try:
619 cls.vapi.connect()
Ole Troan4376ab22021-03-03 10:40:05 +0100620 except (vpp_papi.VPPIOError, Exception) as e:
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500621 cls.logger.debug("Exception connecting to vapi: %s" % e)
622 cls.vapi.disconnect()
623
Klement Sekera085f5c02016-11-24 01:59:16 +0100624 if cls.debug_gdbserver:
625 print(colorize("You're running VPP inside gdbserver but "
626 "VPP-API connection failed, did you forget "
627 "to 'continue' VPP from within gdb?", RED))
Ole Troan4376ab22021-03-03 10:40:05 +0100628 raise e
Klement Sekerae2636852021-03-16 12:52:12 +0100629 if cls.debug_attach:
630 last_line = cls.vapi.cli("show thread").split("\n")[-2]
631 cls.vpp_worker_count = int(last_line.split(" ")[0])
632 print("Detected VPP with %s workers." % cls.vpp_worker_count)
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400633 except vpp_papi.VPPRuntimeError as e:
634 cls.logger.debug("%s" % e)
635 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100636 raise e
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000637 except Exception as e:
638 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400639 cls.quit()
Ole Troan4376ab22021-03-03 10:40:05 +0100640 raise e
Damjan Marionf56b77a2016-10-03 19:44:57 +0200641
Damjan Marionf56b77a2016-10-03 19:44:57 +0200642 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500643 def _debug_quit(cls):
644 if (cls.debug_gdbserver or cls.debug_gdb):
645 try:
646 cls.vpp.poll()
647
648 if cls.vpp.returncode is None:
649 print()
650 print(double_line_delim)
651 print("VPP or GDB server is still running")
652 print(single_line_delim)
653 input("When done debugging, press ENTER to kill the "
654 "process and finish running the testcase...")
655 except AttributeError:
656 pass
657
658 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200659 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200660 """
661 Disconnect vpp-api, kill vpp and cleanup shared memory files
662 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500663 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200664
juraj.linkes184870a2018-07-16 14:22:01 +0200665 # first signal that we want to stop the pump thread, then wake it up
666 if hasattr(cls, 'pump_thread_stop_flag'):
667 cls.pump_thread_stop_flag.set()
668 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100669 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100670 if hasattr(cls, 'pump_thread'):
671 cls.logger.debug("Waiting for pump thread to stop")
672 cls.pump_thread.join()
673 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500674 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100675 cls.vpp_stderr_reader_thread.join()
676
Klement Sekeraf62ae122016-10-11 11:47:09 +0200677 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100678 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100679 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700680 cls.logger.debug("Disconnecting class vapi client on %s",
681 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100682 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700683 cls.logger.debug("Deleting class vapi attribute on %s",
684 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100685 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200686 cls.vpp.poll()
Klement Sekerae2636852021-03-16 12:52:12 +0100687 if not cls.debug_attach and cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000688 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100689 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400690 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100691 cls.logger.debug("Waiting for vpp to die")
Ole Troan4376ab22021-03-03 10:40:05 +0100692 try:
693 outs, errs = cls.vpp.communicate(timeout=5)
694 except subprocess.TimeoutExpired:
695 cls.vpp.kill()
696 outs, errs = cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700697 cls.logger.debug("Deleting class vpp attribute on %s",
698 cls.__name__)
Klement Sekerae2636852021-03-16 12:52:12 +0100699 if not cls.debug_attach:
700 cls.vpp.stdout.close()
701 cls.vpp.stderr.close()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200702 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200703
Klement Sekera3747c752017-04-10 06:30:17 +0200704 if cls.vpp_startup_failed:
705 stdout_log = cls.logger.info
706 stderr_log = cls.logger.critical
707 else:
708 stdout_log = cls.logger.info
709 stderr_log = cls.logger.info
710
Klement Sekerae4504c62016-12-08 10:16:41 +0100711 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200712 stdout_log(single_line_delim)
713 stdout_log('VPP output to stdout while running %s:', cls.__name__)
714 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100715 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200716 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
717 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200718 stdout_log('\n%s', vpp_output)
719 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200720
Klement Sekerae4504c62016-12-08 10:16:41 +0100721 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200722 stderr_log(single_line_delim)
723 stderr_log('VPP output to stderr while running %s:', cls.__name__)
724 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100725 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200726 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
727 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200728 stderr_log('\n%s', vpp_output)
729 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200730
Damjan Marionf56b77a2016-10-03 19:44:57 +0200731 @classmethod
732 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200733 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700734 cls.logger.debug("--- tearDownClass() for %s called ---" %
735 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200736 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200737 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200738 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100739 cls.reset_packet_infos()
Klement Sekerab23ffd72021-05-31 16:08:53 +0200740 if config.debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +0100741 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200742
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700743 def show_commands_at_teardown(self):
744 """ Allow subclass specific teardown logging additions."""
745 self.logger.info("--- No test specific show commands provided. ---")
746
Damjan Marionf56b77a2016-10-03 19:44:57 +0200747 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200748 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100749 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
750 (self.__class__.__name__, self._testMethodName,
751 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700752
753 try:
754 if not self.vpp_dead:
755 self.logger.debug(self.vapi.cli("show trace max 1000"))
756 self.logger.info(self.vapi.ppcli("show interface"))
757 self.logger.info(self.vapi.ppcli("show hardware"))
758 self.logger.info(self.statistics.set_errors_str())
759 self.logger.info(self.vapi.ppcli("show run"))
760 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400761 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700762 self.logger.info("Logging testcase specific show commands.")
763 self.show_commands_at_teardown()
Klement Sekera140af152022-02-18 10:30:51 +0000764 if self.remove_configured_vpp_objects_on_tear_down:
765 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500766 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000767 m = self._testMethodName
768 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500769 tmp_api_trace = "/tmp/%s" % api_trace
770 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
771 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
772 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
773 vpp_api_trace_log))
774 os.rename(tmp_api_trace, vpp_api_trace_log)
Ole Troan4376ab22021-03-03 10:40:05 +0100775 except VppTransportSocketIOError:
776 self.logger.debug("VppTransportSocketIOError: Vpp dead. "
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700777 "Cannot log show commands.")
778 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100779 else:
780 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200781
Damjan Marionf56b77a2016-10-03 19:44:57 +0200782 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200783 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800784 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200785 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100786 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400787 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
788 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100789 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100790 self.vpp_stdout_deque.append(
791 "--- test setUp() for %s.%s(%s) starts here ---\n" %
792 (self.__class__.__name__, self._testMethodName,
793 self._testMethodDoc))
794 self.vpp_stderr_deque.append(
795 "--- test setUp() for %s.%s(%s) starts here ---\n" %
796 (self.__class__.__name__, self._testMethodName,
797 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200798 self.vapi.cli("clear trace")
799 # store the test instance inside the test class - so that objects
800 # holding the class can access instance methods (like assertEqual)
801 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200802
Damjan Marionf56b77a2016-10-03 19:44:57 +0200803 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200804 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200805 """
806 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200807
Klement Sekera75e7d132017-09-20 08:26:30 +0200808 :param interfaces: iterable interface indexes (if None,
809 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200810
Klement Sekeraf62ae122016-10-11 11:47:09 +0200811 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200812 if interfaces is None:
813 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200814 for i in interfaces:
815 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200816
Damjan Marionf56b77a2016-10-03 19:44:57 +0200817 @classmethod
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200818 def register_pcap(cls, intf, worker):
819 """ Register a pcap in the testclass """
Klement Sekera9225dee2016-12-12 08:36:58 +0100820 # add to the list of captures with current timestamp
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200821 cls._pcaps.append((intf, worker))
Klement Sekera9225dee2016-12-12 08:36:58 +0100822
823 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000824 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400825 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
826 # returns float("2.190522")
827 timestr = cls.vapi.cli('show clock')
828 head, sep, tail = timestr.partition(',')
829 head, sep, tail = head.partition('Time now')
830 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000831
832 @classmethod
833 def sleep_on_vpp_time(cls, sec):
834 """ Sleep according to time in VPP world """
835 # On a busy system with many processes
836 # we might end up with VPP time being slower than real world
837 # So take that into account when waiting for VPP to do something
838 start_time = cls.get_vpp_time()
839 while cls.get_vpp_time() - start_time < sec:
840 cls.sleep(0.1)
841
842 @classmethod
Benoît Ganne8c45e512021-02-19 16:39:13 +0100843 def pg_start(cls, trace=True):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000844 """ Enable the PG, wait till it is done, then clean up """
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200845 for (intf, worker) in cls._old_pcaps:
Klement Sekerab23ffd72021-05-31 16:08:53 +0200846 intf.handle_old_pcap_file(intf.get_in_path(worker),
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200847 intf.in_history_counter)
848 cls._old_pcaps = []
Benoît Ganne8c45e512021-02-19 16:39:13 +0100849 if trace:
850 cls.vapi.cli("clear trace")
851 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200852 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000853 # PG, when starts, runs to completion -
854 # so let's avoid a race condition,
855 # and wait a little till it's done.
856 # Then clean it up - and then be gone.
857 deadline = time.time() + 300
858 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
859 cls.sleep(0.01) # yield
860 if time.time() > deadline:
861 cls.logger.error("Timeout waiting for pg to stop")
862 break
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200863 for intf, worker in cls._pcaps:
Klement Sekera7ba9fae2021-03-31 13:36:38 +0200864 cls.vapi.cli('packet-generator delete %s' %
865 intf.get_cap_name(worker))
Klement Sekera3ff6ffc2021-04-01 18:19:29 +0200866 cls._old_pcaps = cls._pcaps
867 cls._pcaps = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200868
Damjan Marionf56b77a2016-10-03 19:44:57 +0200869 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000870 def create_pg_interfaces_internal(cls, interfaces, gso=0, gso_size=0,
871 mode=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200872 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100873 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200874
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100875 :param interfaces: iterable indexes of the interfaces.
876 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200877
Klement Sekeraf62ae122016-10-11 11:47:09 +0200878 """
879 result = []
880 for i in interfaces:
Neale Ranns6197cb72021-06-03 14:43:21 +0000881 intf = VppPGInterface(cls, i, gso, gso_size, mode)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200882 setattr(cls, intf.name, intf)
883 result.append(intf)
884 cls.pg_interfaces = result
885 return result
886
Matej Klotton0178d522016-11-04 11:11:44 +0100887 @classmethod
Neale Ranns6197cb72021-06-03 14:43:21 +0000888 def create_pg_ip4_interfaces(cls, interfaces, gso=0, gso_size=0):
889 pgmode = VppEnum.vl_api_pg_interface_mode_t
890 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
891 pgmode.PG_API_MODE_IP4)
892
893 @classmethod
894 def create_pg_ip6_interfaces(cls, interfaces, gso=0, gso_size=0):
895 pgmode = VppEnum.vl_api_pg_interface_mode_t
896 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
897 pgmode.PG_API_MODE_IP6)
898
899 @classmethod
900 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
901 pgmode = VppEnum.vl_api_pg_interface_mode_t
902 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
903 pgmode.PG_API_MODE_ETHERNET)
904
905 @classmethod
906 def create_pg_ethernet_interfaces(cls, interfaces, gso=0, gso_size=0):
907 pgmode = VppEnum.vl_api_pg_interface_mode_t
908 return cls.create_pg_interfaces_internal(interfaces, gso, gso_size,
909 pgmode.PG_API_MODE_ETHERNET)
910
911 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200912 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100913 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100914 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100915
Klement Sekerab9ef2732018-06-24 22:49:33 +0200916 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100917 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100918 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200919 result = [VppLoInterface(cls) for i in range(count)]
920 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100921 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100922 cls.lo_interfaces = result
923 return result
924
Neale Ranns192b13f2019-03-15 02:16:20 -0700925 @classmethod
926 def create_bvi_interfaces(cls, count):
927 """
928 Create BVI interfaces.
929
930 :param count: number of interfaces created.
931 :returns: List of created interfaces.
932 """
933 result = [VppBviInterface(cls) for i in range(count)]
934 for intf in result:
935 setattr(cls, intf.name, intf)
936 cls.bvi_interfaces = result
937 return result
938
Damjan Marionf56b77a2016-10-03 19:44:57 +0200939 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200940 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200941 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200942 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200943 NOTE: Currently works only when Raw layer is present.
944
945 :param packet: packet
946 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200947 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200948
949 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200950 packet_len = len(packet) + 4
951 extend = size - packet_len
952 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200953 num = (extend // len(padding)) + 1
954 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200955
Klement Sekeradab231a2016-12-21 08:50:14 +0100956 @classmethod
957 def reset_packet_infos(cls):
958 """ Reset the list of packet info objects and packet counts to zero """
959 cls._packet_infos = {}
960 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200961
Klement Sekeradab231a2016-12-21 08:50:14 +0100962 @classmethod
963 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200964 """
965 Create packet info object containing the source and destination indexes
966 and add it to the testcase's packet info list
967
Klement Sekeradab231a2016-12-21 08:50:14 +0100968 :param VppInterface src_if: source interface
969 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200970
971 :returns: _PacketInfo object
972
973 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200974 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100975 info.index = len(cls._packet_infos)
976 info.src = src_if.sw_if_index
977 info.dst = dst_if.sw_if_index
978 if isinstance(dst_if, VppSubInterface):
979 dst_idx = dst_if.parent.sw_if_index
980 else:
981 dst_idx = dst_if.sw_if_index
982 if dst_idx in cls._packet_count_for_dst_if_idx:
983 cls._packet_count_for_dst_if_idx[dst_idx] += 1
984 else:
985 cls._packet_count_for_dst_if_idx[dst_idx] = 1
986 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200987 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200988
Damjan Marionf56b77a2016-10-03 19:44:57 +0200989 @staticmethod
990 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200991 """
992 Convert _PacketInfo object to packet payload
993
994 :param info: _PacketInfo object
995
996 :returns: string containing serialized data from packet info
997 """
Ray Kinsellab8165b92021-09-22 11:24:06 +0100998
999 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1000 return pack('iiiih', info.index, info.src,
1001 info.dst, info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001002
Damjan Marionf56b77a2016-10-03 19:44:57 +02001003 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001004 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001005 """
1006 Convert packet payload to _PacketInfo object
1007
1008 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001009 :type payload: <class 'scapy.packet.Raw'>
1010 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -08001011 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -07001012 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +02001013 :returns: _PacketInfo object containing de-serialized data from payload
1014
1015 """
Ray Kinsellab8165b92021-09-22 11:24:06 +01001016
1017 # retrieve payload, currently 18 bytes (4 x ints + 1 short)
1018 payload_b = getattr(payload, payload_field)[:18]
1019
Damjan Marionf56b77a2016-10-03 19:44:57 +02001020 info = _PacketInfo()
Ray Kinsellab8165b92021-09-22 11:24:06 +01001021 info.index, info.src, info.dst, info.ip, info.proto \
1022 = unpack('iiiih', payload_b)
1023
1024 # some SRv6 TCs depend on get an exception if bad values are detected
1025 if info.index > 0x4000:
1026 raise ValueError('Index value is invalid')
1027
Damjan Marionf56b77a2016-10-03 19:44:57 +02001028 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001029
Damjan Marionf56b77a2016-10-03 19:44:57 +02001030 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001031 """
1032 Iterate over the packet info list stored in the testcase
1033 Start iteration with first element if info is None
1034 Continue based on index in info if info is specified
1035
1036 :param info: info or None
1037 :returns: next info in list or None if no more infos
1038 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001039 if info is None:
1040 next_index = 0
1041 else:
1042 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +01001043 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +02001044 return None
1045 else:
Klement Sekeradab231a2016-12-21 08:50:14 +01001046 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001047
Klement Sekeraf62ae122016-10-11 11:47:09 +02001048 def get_next_packet_info_for_interface(self, src_index, info):
1049 """
1050 Search the packet info list for the next packet info with same source
1051 interface index
1052
1053 :param src_index: source interface index to search for
1054 :param info: packet info - where to start the search
1055 :returns: packet info or None
1056
1057 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001058 while True:
1059 info = self.get_next_packet_info(info)
1060 if info is None:
1061 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001062 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001063 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001064
Klement Sekeraf62ae122016-10-11 11:47:09 +02001065 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1066 """
1067 Search the packet info list for the next packet info with same source
1068 and destination interface indexes
1069
1070 :param src_index: source interface index to search for
1071 :param dst_index: destination interface index to search for
1072 :param info: packet info - where to start the search
1073 :returns: packet info or None
1074
1075 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001076 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001077 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001078 if info is None:
1079 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001080 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001081 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001082
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001083 def assert_equal(self, real_value, expected_value, name_or_class=None):
1084 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001085 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001086 return
1087 try:
1088 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1089 msg = msg % (getdoc(name_or_class).strip(),
1090 real_value, str(name_or_class(real_value)),
1091 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001092 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001093 msg = "Invalid %s: %s does not match expected value %s" % (
1094 name_or_class, real_value, expected_value)
1095
1096 self.assertEqual(real_value, expected_value, msg)
1097
Klement Sekerab17dd962017-01-09 07:43:48 +01001098 def assert_in_range(self,
1099 real_value,
1100 expected_min,
1101 expected_max,
1102 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001103 if name is None:
1104 msg = None
1105 else:
1106 msg = "Invalid %s: %s out of range <%s,%s>" % (
1107 name, real_value, expected_min, expected_max)
1108 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1109
Klement Sekerad81ae412018-05-16 10:52:54 +02001110 def assert_packet_checksums_valid(self, packet,
1111 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001112 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001113 udp_layers = ['UDP', 'UDPerror']
1114 checksum_fields = ['cksum', 'chksum']
1115 checksums = []
1116 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001117 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001118 while True:
1119 layer = temp.getlayer(counter)
1120 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001121 layer = layer.copy()
1122 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001123 for cf in checksum_fields:
1124 if hasattr(layer, cf):
1125 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001126 0 == getattr(layer, cf) and \
1127 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001128 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001129 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001130 checksums.append((counter, cf))
1131 else:
1132 break
1133 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001134 if 0 == len(checksums):
1135 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001136 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001137 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001138 calc_sum = getattr(temp[layer], cf)
1139 self.assert_equal(
1140 getattr(received[layer], cf), calc_sum,
1141 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1142 self.logger.debug(
1143 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1144 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001145
1146 def assert_checksum_valid(self, received_packet, layer,
1147 field_name='chksum',
1148 ignore_zero_checksum=False):
1149 """ Check checksum of received packet on given layer """
1150 received_packet_checksum = getattr(received_packet[layer], field_name)
1151 if ignore_zero_checksum and 0 == received_packet_checksum:
1152 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001153 recalculated = received_packet.__class__(
1154 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001155 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001156 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001157 self.assert_equal(received_packet_checksum,
1158 getattr(recalculated[layer], field_name),
1159 "packet checksum on layer: %s" % layer)
1160
1161 def assert_ip_checksum_valid(self, received_packet,
1162 ignore_zero_checksum=False):
1163 self.assert_checksum_valid(received_packet, 'IP',
1164 ignore_zero_checksum=ignore_zero_checksum)
1165
1166 def assert_tcp_checksum_valid(self, received_packet,
1167 ignore_zero_checksum=False):
1168 self.assert_checksum_valid(received_packet, 'TCP',
1169 ignore_zero_checksum=ignore_zero_checksum)
1170
1171 def assert_udp_checksum_valid(self, received_packet,
1172 ignore_zero_checksum=True):
1173 self.assert_checksum_valid(received_packet, 'UDP',
1174 ignore_zero_checksum=ignore_zero_checksum)
1175
1176 def assert_embedded_icmp_checksum_valid(self, received_packet):
1177 if received_packet.haslayer(IPerror):
1178 self.assert_checksum_valid(received_packet, 'IPerror')
1179 if received_packet.haslayer(TCPerror):
1180 self.assert_checksum_valid(received_packet, 'TCPerror')
1181 if received_packet.haslayer(UDPerror):
1182 self.assert_checksum_valid(received_packet, 'UDPerror',
1183 ignore_zero_checksum=True)
1184 if received_packet.haslayer(ICMPerror):
1185 self.assert_checksum_valid(received_packet, 'ICMPerror')
1186
1187 def assert_icmp_checksum_valid(self, received_packet):
1188 self.assert_checksum_valid(received_packet, 'ICMP')
1189 self.assert_embedded_icmp_checksum_valid(received_packet)
1190
1191 def assert_icmpv6_checksum_valid(self, pkt):
1192 if pkt.haslayer(ICMPv6DestUnreach):
1193 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1194 self.assert_embedded_icmp_checksum_valid(pkt)
1195 if pkt.haslayer(ICMPv6EchoRequest):
1196 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1197 if pkt.haslayer(ICMPv6EchoReply):
1198 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1199
Klement Sekera107ad732022-02-18 10:32:08 +00001200 def get_counter(self, counter):
Klement Sekera3a343d42019-05-16 14:35:46 +02001201 if counter.startswith("/"):
1202 counter_value = self.statistics.get_counter(counter)
1203 else:
1204 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001205 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001206 for i in range(1, len(counters) - 1):
1207 results = counters[i].split()
1208 if results[1] == counter:
1209 counter_value = int(results[0])
1210 break
1211 return counter_value
1212
Klement Sekera107ad732022-02-18 10:32:08 +00001213 def assert_counter_equal(self, counter, expected_value,
1214 thread=None, index=0):
1215 c = self.get_counter(counter)
1216 if thread is not None:
1217 c = c[thread][index]
1218 else:
1219 c = sum(x[index] for x in c)
1220 self.assert_equal(c, expected_value, "counter `%s'" % counter)
1221
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001222 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera107ad732022-02-18 10:32:08 +00001223 counter_value = self.get_counter(counter)
Klement Sekera6aa58b72019-05-16 14:34:55 +02001224 self.assert_equal(counter_value, expected_value,
1225 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001226
Ole Troan233e4682019-05-16 15:01:34 +02001227 def assert_error_counter_equal(self, counter, expected_value):
Ole Troane66443c2021-03-18 11:12:01 +01001228 counter_value = self.statistics[counter].sum()
Ole Troan233e4682019-05-16 15:01:34 +02001229 self.assert_equal(counter_value, expected_value,
1230 "error counter `%s'" % counter)
1231
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001232 @classmethod
1233 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001234
1235 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1236 # * by Guido, only the main thread can be interrupted.
1237 # */
1238 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1239 if timeout == 0:
1240 # yield quantum
1241 if hasattr(os, 'sched_yield'):
1242 os.sched_yield()
1243 else:
1244 time.sleep(0)
1245 return
1246
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001247 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001248 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001249 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001250 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001251 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001252 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001253 "slept for %es instead of ~%es!",
1254 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001255
1256 cls.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001257 "Finished sleep (%s) - slept %es (wanted %es)",
1258 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001259
Benoît Ganne56eccdb2021-08-20 09:18:31 +02001260 def virtual_sleep(self, timeout, remark=None):
1261 self.logger.debug("Moving VPP time by %s (%s)", timeout, remark)
1262 self.vapi.cli("set clock adjust %s" % timeout)
1263
Benoît Ganne8c45e512021-02-19 16:39:13 +01001264 def pg_send(self, intf, pkts, worker=None, trace=True):
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001265 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001266 self.pg_enable_capture(self.pg_interfaces)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001267 self.pg_start(trace=trace)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001268
Klement Sekeraad3187f2022-02-18 10:34:35 +00001269 def snapshot_stats(self, stats_diff):
1270 """Return snapshot of interesting stats based on diff dictionary."""
1271 stats_snapshot = {}
1272 for sw_if_index in stats_diff:
1273 for counter in stats_diff[sw_if_index]:
1274 stats_snapshot[counter] = self.statistics[counter]
1275 self.logger.debug(f"Took statistics stats_snapshot: {stats_snapshot}")
1276 return stats_snapshot
1277
1278 def compare_stats_with_snapshot(self, stats_diff, stats_snapshot):
1279 """Assert appropriate difference between current stats and snapshot."""
1280 for sw_if_index in stats_diff:
1281 for cntr, diff in stats_diff[sw_if_index].items():
1282 if sw_if_index == "err":
1283 self.assert_equal(
1284 self.statistics[cntr].sum(),
1285 stats_snapshot[cntr].sum() + diff,
1286 f"'{cntr}' counter value (previous value: "
1287 f"{stats_snapshot[cntr].sum()}, "
1288 f"expected diff: {diff})")
1289 else:
1290 try:
1291 self.assert_equal(
1292 self.statistics[cntr][:, sw_if_index].sum(),
1293 stats_snapshot[cntr][:, sw_if_index].sum() + diff,
1294 f"'{cntr}' counter value (previous value: "
1295 f"{stats_snapshot[cntr][:, sw_if_index].sum()}, "
1296 f"expected diff: {diff})")
1297 except IndexError:
1298 # if diff is 0, then this most probably a case where
1299 # test declares multiple interfaces but traffic hasn't
1300 # passed through this one yet - which means the counter
1301 # value is 0 and can be ignored
1302 if 0 != diff:
1303 raise
1304
Klement Sekera7c3275e2021-12-07 09:49:53 +00001305 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None,
Klement Sekeraad3187f2022-02-18 10:34:35 +00001306 stats_diff=None, trace=True, msg=None):
1307 if stats_diff:
1308 stats_snapshot = self.snapshot_stats(stats_diff)
1309
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001310 self.pg_send(intf, pkts)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001311
1312 try:
1313 if not timeout:
1314 timeout = 1
1315 for i in self.pg_interfaces:
Klement Sekera26cd0242022-02-18 10:35:08 +00001316 i.assert_nothing_captured(timeout=timeout, remark=remark)
Klement Sekeraad3187f2022-02-18 10:34:35 +00001317 timeout = 0.1
1318 finally:
1319 if trace:
1320 if msg:
1321 self.logger.debug(f"send_and_assert_no_replies: {msg}")
1322 self.logger.debug(self.vapi.cli("show trace"))
1323
1324 if stats_diff:
1325 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
Neale Ranns52fae862018-01-08 04:41:42 -08001326
Benoît Ganne8c45e512021-02-19 16:39:13 +01001327 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
Klement Sekeraad3187f2022-02-18 10:34:35 +00001328 trace=True, msg=None, stats_diff=None):
1329 if stats_diff:
1330 stats_snapshot = self.snapshot_stats(stats_diff)
1331
Neale Rannsd7603d92019-03-28 08:56:10 +00001332 if not n_rx:
Klement Sekera56c492a2022-01-10 21:57:27 +00001333 n_rx = 1 if isinstance(pkts, Packet) else len(pkts)
Benoît Ganne8c45e512021-02-19 16:39:13 +01001334 self.pg_send(intf, pkts, worker=worker, trace=trace)
Neale Rannsd7603d92019-03-28 08:56:10 +00001335 rx = output.get_capture(n_rx)
Klement Sekeraff334db2021-05-26 13:02:35 +02001336 if trace:
Klement Sekeraad3187f2022-02-18 10:34:35 +00001337 if msg:
1338 self.logger.debug(f"send_and_expect: {msg}")
Klement Sekeraff334db2021-05-26 13:02:35 +02001339 self.logger.debug(self.vapi.cli("show trace"))
Klement Sekeraad3187f2022-02-18 10:34:35 +00001340
1341 if stats_diff:
1342 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1343
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001344 return rx
1345
Neale Ranns699bea22022-02-17 09:22:16 +00001346 def send_and_expect_load_balancing(self, input, pkts, outputs,
1347 worker=None, trace=True):
1348 self.pg_send(input, pkts, worker=worker, trace=trace)
1349 rxs = []
1350 for oo in outputs:
1351 rx = oo._get_capture(1)
1352 self.assertNotEqual(0, len(rx))
1353 rxs.append(rx)
1354 if trace:
1355 self.logger.debug(self.vapi.cli("show trace"))
1356 return rxs
1357
Neale Ranns5c6dd172022-02-17 09:08:47 +00001358 def send_and_expect_some(self, intf, pkts, output,
1359 worker=None,
1360 trace=True):
1361 self.pg_send(intf, pkts, worker=worker, trace=trace)
1362 rx = output._get_capture(1)
1363 if trace:
1364 self.logger.debug(self.vapi.cli("show trace"))
1365 self.assertTrue(len(rx) > 0)
1366 self.assertTrue(len(rx) < len(pkts))
1367 return rx
1368
Klement Sekeraad3187f2022-02-18 10:34:35 +00001369 def send_and_expect_only(self, intf, pkts, output, timeout=None,
1370 stats_diff=None):
1371 if stats_diff:
1372 stats_snapshot = self.snapshot_stats(stats_diff)
1373
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001374 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001375 rx = output.get_capture(len(pkts))
1376 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001377 if not timeout:
1378 timeout = 1
1379 for i in self.pg_interfaces:
1380 if i not in outputs:
Klement Sekera26cd0242022-02-18 10:35:08 +00001381 i.assert_nothing_captured(timeout=timeout)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001382 timeout = 0.1
1383
Klement Sekeraad3187f2022-02-18 10:34:35 +00001384 if stats_diff:
1385 self.compare_stats_with_snapshot(stats_diff, stats_snapshot)
1386
Neale Ranns52fae862018-01-08 04:41:42 -08001387 return rx
1388
Damjan Marionf56b77a2016-10-03 19:44:57 +02001389
juraj.linkes184870a2018-07-16 14:22:01 +02001390def get_testcase_doc_name(test):
1391 return getdoc(test.__class__).splitlines()[0]
1392
1393
Ole Trøan5ba91592018-11-22 10:01:09 +00001394def get_test_description(descriptions, test):
1395 short_description = test.shortDescription()
1396 if descriptions and short_description:
1397 return short_description
1398 else:
1399 return str(test)
1400
1401
juraj.linkes40dd73b2018-09-21 13:55:16 +02001402class TestCaseInfo(object):
1403 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1404 self.logger = logger
1405 self.tempdir = tempdir
1406 self.vpp_pid = vpp_pid
1407 self.vpp_bin_path = vpp_bin_path
1408 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001409
1410
Damjan Marionf56b77a2016-10-03 19:44:57 +02001411class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001412 """
1413 @property result_string
1414 String variable to store the test case result string.
1415 @property errors
1416 List variable containing 2-tuples of TestCase instances and strings
1417 holding formatted tracebacks. Each tuple represents a test which
1418 raised an unexpected exception.
1419 @property failures
1420 List variable containing 2-tuples of TestCase instances and strings
1421 holding formatted tracebacks. Each tuple represents a test where
1422 a failure was explicitly signalled using the TestCase.assert*()
1423 methods.
1424 """
1425
juraj.linkes40dd73b2018-09-21 13:55:16 +02001426 failed_test_cases_info = set()
1427 core_crash_test_cases_info = set()
1428 current_test_case_info = None
1429
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001430 def __init__(self, stream=None, descriptions=None, verbosity=None,
1431 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001432 """
Klement Sekerada505f62017-01-04 12:58:53 +01001433 :param stream File descriptor to store where to report test results.
1434 Set to the standard error stream by default.
1435 :param descriptions Boolean variable to store information if to use
1436 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001437 :param verbosity Integer variable to store required verbosity level.
1438 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001439 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001440 self.stream = stream
1441 self.descriptions = descriptions
1442 self.verbosity = verbosity
1443 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001444 self.runner = runner
Klement Sekera558ceab2021-04-08 19:37:41 +02001445 self.printed = []
Damjan Marionf56b77a2016-10-03 19:44:57 +02001446
Damjan Marionf56b77a2016-10-03 19:44:57 +02001447 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001448 """
1449 Record a test succeeded result
1450
1451 :param test:
1452
1453 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001454 if self.current_test_case_info:
1455 self.current_test_case_info.logger.debug(
1456 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1457 test._testMethodName,
1458 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001459 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001460 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001461
juraj.linkescae64f82018-09-19 15:01:47 +02001462 self.send_result_through_pipe(test, PASS)
1463
Klement Sekeraf62ae122016-10-11 11:47:09 +02001464 def addSkip(self, test, reason):
1465 """
1466 Record a test skipped.
1467
1468 :param test:
1469 :param reason:
1470
1471 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001472 if self.current_test_case_info:
1473 self.current_test_case_info.logger.debug(
1474 "--- addSkip() %s.%s(%s) called, reason is %s" %
1475 (test.__class__.__name__, test._testMethodName,
1476 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001477 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001478 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001479
Klement Sekera558ceab2021-04-08 19:37:41 +02001480 if reason == "not enough cpus":
1481 self.send_result_through_pipe(test, SKIP_CPU_SHORTAGE)
1482 else:
1483 self.send_result_through_pipe(test, SKIP)
juraj.linkescae64f82018-09-19 15:01:47 +02001484
juraj.linkes40dd73b2018-09-21 13:55:16 +02001485 def symlink_failed(self):
1486 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001487 try:
Klement Sekerab23ffd72021-05-31 16:08:53 +02001488 failed_dir = config.failed_dir
juraj.linkes40dd73b2018-09-21 13:55:16 +02001489 link_path = os.path.join(
1490 failed_dir,
1491 '%s-FAILED' %
1492 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001493
1494 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001495 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001496 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001497 "os.symlink(%s, %s)" %
1498 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001499 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001500 self.current_test_case_info.logger.debug(
Klement Sekera79a31db2021-03-12 18:16:10 +01001501 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001502 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001503 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001504
Klement Sekeraf413bef2017-08-15 07:09:02 +02001505 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001506 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001507
juraj.linkescae64f82018-09-19 15:01:47 +02001508 def send_result_through_pipe(self, test, result):
1509 if hasattr(self, 'test_framework_result_pipe'):
1510 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001511 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001512 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001513
juraj.linkes40dd73b2018-09-21 13:55:16 +02001514 def log_error(self, test, err, fn_name):
1515 if self.current_test_case_info:
1516 if isinstance(test, unittest.suite._ErrorHolder):
1517 test_name = test.description
1518 else:
1519 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1520 test._testMethodName,
1521 test._testMethodDoc)
1522 self.current_test_case_info.logger.debug(
1523 "--- %s() %s called, err is %s" %
1524 (fn_name, test_name, err))
1525 self.current_test_case_info.logger.debug(
1526 "formatted exception is:\n%s" %
1527 "".join(format_exception(*err)))
1528
1529 def add_error(self, test, err, unittest_fn, error_type):
1530 if error_type == FAIL:
1531 self.log_error(test, err, 'addFailure')
1532 error_type_str = colorize("FAIL", RED)
1533 elif error_type == ERROR:
1534 self.log_error(test, err, 'addError')
1535 error_type_str = colorize("ERROR", RED)
1536 else:
1537 raise Exception('Error type %s cannot be used to record an '
1538 'error or a failure' % error_type)
1539
1540 unittest_fn(self, test, err)
1541 if self.current_test_case_info:
1542 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1543 (error_type_str,
1544 self.current_test_case_info.tempdir)
1545 self.symlink_failed()
1546 self.failed_test_cases_info.add(self.current_test_case_info)
1547 if is_core_present(self.current_test_case_info.tempdir):
1548 if not self.current_test_case_info.core_crash_test:
1549 if isinstance(test, unittest.suite._ErrorHolder):
1550 test_name = str(test)
1551 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001552 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001553 get_testcase_doc_name(test), test.id())
1554 self.current_test_case_info.core_crash_test = test_name
1555 self.core_crash_test_cases_info.add(
1556 self.current_test_case_info)
1557 else:
1558 self.result_string = '%s [no temp dir]' % error_type_str
1559
1560 self.send_result_through_pipe(test, error_type)
1561
Damjan Marionf56b77a2016-10-03 19:44:57 +02001562 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001563 """
1564 Record a test failed result
1565
1566 :param test:
1567 :param err: error message
1568
1569 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001570 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001571
Damjan Marionf56b77a2016-10-03 19:44:57 +02001572 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001573 """
1574 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001575
Klement Sekeraf62ae122016-10-11 11:47:09 +02001576 :param test:
1577 :param err: error message
1578
1579 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001580 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001581
Damjan Marionf56b77a2016-10-03 19:44:57 +02001582 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001583 """
1584 Get test description
1585
1586 :param test:
1587 :returns: test description
1588
1589 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001590 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001591
Damjan Marionf56b77a2016-10-03 19:44:57 +02001592 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001593 """
1594 Start a test
1595
1596 :param test:
1597
1598 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001599
1600 def print_header(test):
Klement Sekera558ceab2021-04-08 19:37:41 +02001601 if test.__class__ in self.printed:
1602 return
1603
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001604 test_doc = getdoc(test)
1605 if not test_doc:
1606 raise Exception("No doc string for test '%s'" % test.id())
Klement Sekera558ceab2021-04-08 19:37:41 +02001607
Klement Sekeraea6236b2021-03-25 14:03:44 +01001608 test_title = test_doc.splitlines()[0].rstrip()
Klement Sekera558ceab2021-04-08 19:37:41 +02001609 test_title = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001610 if test.is_tagged_run_solo():
Klement Sekera558ceab2021-04-08 19:37:41 +02001611 test_title = colorize(f"SOLO RUN: {test_title}", YELLOW)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001612
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001613 # This block may overwrite the colorized title above,
1614 # but we want this to stand out and be fixed
1615 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
Klement Sekera558ceab2021-04-08 19:37:41 +02001616 test_title = colorize(
1617 f"FIXME with VPP workers: {test_title}", RED)
Andrew Yourtchenkoc8eae8d2021-01-20 20:30:36 +00001618
Naveen Joy6eaeea92021-09-09 17:57:02 -07001619 if test.has_tag(TestCaseTag.FIXME_ASAN):
1620 test_title = colorize(
1621 f"FIXME with ASAN: {test_title}", RED)
1622 test.skip_fixme_asan()
1623
Klement Sekeraea6236b2021-03-25 14:03:44 +01001624 if hasattr(test, 'vpp_worker_count'):
1625 if test.vpp_worker_count == 0:
1626 test_title += " [main thread only]"
1627 elif test.vpp_worker_count == 1:
1628 test_title += " [1 worker thread]"
1629 else:
1630 test_title += f" [{test.vpp_worker_count} worker threads]"
1631
Klement Sekera558ceab2021-04-08 19:37:41 +02001632 if test.__class__.skipped_due_to_cpu_lack:
1633 test_title = colorize(
1634 f"{test_title} [skipped - not enough cpus, "
1635 f"required={test.__class__.get_cpus_required()}, "
1636 f"available={max_vpp_cpus}]", YELLOW)
1637
1638 print(double_line_delim)
1639 print(test_title)
1640 print(double_line_delim)
1641 self.printed.append(test.__class__)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001642
1643 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001644 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001645 unittest.TestResult.startTest(self, test)
1646 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001647 self.stream.writeln(
1648 "Starting " + self.getDescription(test) + " ...")
1649 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001650
Damjan Marionf56b77a2016-10-03 19:44:57 +02001651 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001652 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001653 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001654
1655 :param test:
1656
1657 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001658 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001659
Damjan Marionf56b77a2016-10-03 19:44:57 +02001660 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001661 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001662 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001663 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001664 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001665 else:
Ole Troan0c629322019-11-28 14:48:44 +01001666 self.stream.writeln("%-68s %4.2f %s" %
1667 (self.getDescription(test),
1668 time.time() - self.start_test,
1669 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001670
1671 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001672
Damjan Marionf56b77a2016-10-03 19:44:57 +02001673 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001674 """
1675 Print errors from running the test case
1676 """
juraj.linkesabec0122018-11-16 17:28:56 +01001677 if len(self.errors) > 0 or len(self.failures) > 0:
1678 self.stream.writeln()
1679 self.printErrorList('ERROR', self.errors)
1680 self.printErrorList('FAIL', self.failures)
1681
1682 # ^^ that is the last output from unittest before summary
1683 if not self.runner.print_summary:
1684 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1685 self.stream = devnull
1686 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001687
Damjan Marionf56b77a2016-10-03 19:44:57 +02001688 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001689 """
1690 Print error list to the output stream together with error type
1691 and test case description.
1692
1693 :param flavour: error type
1694 :param errors: iterable errors
1695
1696 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001697 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001698 self.stream.writeln(double_line_delim)
1699 self.stream.writeln("%s: %s" %
1700 (flavour, self.getDescription(test)))
1701 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001702 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001703
1704
Damjan Marionf56b77a2016-10-03 19:44:57 +02001705class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001706 """
Klement Sekera104543f2017-02-03 07:29:43 +01001707 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001708 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001709
Klement Sekeraf62ae122016-10-11 11:47:09 +02001710 @property
1711 def resultclass(self):
1712 """Class maintaining the results of the tests"""
1713 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001714
juraj.linkes184870a2018-07-16 14:22:01 +02001715 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001716 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001717 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001718 # ignore stream setting here, use hard-coded stdout to be in sync
1719 # with prints from VppTestCase methods ...
1720 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1721 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001722 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001723 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001724
juraj.linkesabec0122018-11-16 17:28:56 +01001725 self.orig_stream = self.stream
1726 self.resultclass.test_framework_result_pipe = result_pipe
1727
1728 self.print_summary = print_summary
1729
1730 def _makeResult(self):
1731 return self.resultclass(self.stream,
1732 self.descriptions,
1733 self.verbosity,
1734 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001735
Damjan Marionf56b77a2016-10-03 19:44:57 +02001736 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001737 """
1738 Run the tests
1739
1740 :param test:
1741
1742 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001743 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001744
1745 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001746 if not self.print_summary:
1747 self.stream = self.orig_stream
1748 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001749 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001750
1751
1752class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001753 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1754 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001755 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001756 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001757 if hasattr(self, 'testcase') and self.testcase.debug_all:
1758 if self.testcase.debug_gdbserver:
1759 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1760 .format(port=self.testcase.gdbserver_port)] + args
1761 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1762 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001763 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001764 self.app_name = os.path.basename(self.app_bin)
1765 if hasattr(self, 'role'):
1766 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001767 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001768 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001769 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001770 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001771
Dave Wallace24564332019-10-21 02:53:14 +00001772 def wait_for_enter(self):
1773 if not hasattr(self, 'testcase'):
1774 return
1775 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1776 print()
1777 print(double_line_delim)
1778 print("Spawned GDB Server for '{app}' with PID: {pid}"
1779 .format(app=self.app_name, pid=self.process.pid))
1780 elif self.testcase.debug_all and self.testcase.debug_gdb:
1781 print()
1782 print(double_line_delim)
1783 print("Spawned '{app}' with PID: {pid}"
1784 .format(app=self.app_name, pid=self.process.pid))
1785 else:
1786 return
1787 print(single_line_delim)
1788 print("You can debug '{app}' using:".format(app=self.app_name))
1789 if self.testcase.debug_gdbserver:
1790 print("sudo gdb " + self.app_bin +
1791 " -ex 'target remote localhost:{port}'"
1792 .format(port=self.testcase.gdbserver_port))
1793 print("Now is the time to attach gdb by running the above "
1794 "command, set up breakpoints etc., then resume from "
1795 "within gdb by issuing the 'continue' command")
1796 self.testcase.gdbserver_port += 1
1797 elif self.testcase.debug_gdb:
1798 print("sudo gdb " + self.app_bin +
1799 " -ex 'attach {pid}'".format(pid=self.process.pid))
1800 print("Now is the time to attach gdb by running the above "
1801 "command and set up breakpoints etc., then resume from"
1802 " within gdb by issuing the 'continue' command")
1803 print(single_line_delim)
1804 input("Press ENTER to continue running the testcase...")
1805
Neale Ranns812ed392017-10-16 04:20:13 -07001806 def run(self):
1807 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001808 if not os.path.exists(executable) or not os.access(
1809 executable, os.F_OK | os.X_OK):
1810 # Exit code that means some system file did not exist,
1811 # could not be opened, or had some other kind of error.
1812 self.result = os.EX_OSFILE
1813 raise EnvironmentError(
1814 "executable '%s' is not found or executable." % executable)
Dave Wallace4ee17d82021-05-20 14:01:51 -04001815 self.logger.debug("Running executable '{app}': '{cmd}'"
1816 .format(app=self.app_name,
1817 cmd=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001818 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001819 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001820 env["CK_LOG_FILE_NAME"] = "-"
1821 self.process = subprocess.Popen(
Dave Wallaceae8d3322021-05-18 17:12:16 -04001822 ['stdbuf', '-o0', '-e0'] + self.args, shell=False, env=env,
1823 preexec_fn=os.setpgrp, stdout=subprocess.PIPE,
1824 stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001825 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001826 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001827 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001828 self.logger.info("Return code is `%s'" % self.process.returncode)
1829 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001830 self.logger.info("Executable `{app}' wrote to stdout:"
1831 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001832 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001833 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001834 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001835 self.logger.info("Executable `{app}' wrote to stderr:"
1836 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001837 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001838 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001839 self.logger.info(single_line_delim)
1840 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001841
Klement Sekera6aa58b72019-05-16 14:34:55 +02001842
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001843if __name__ == '__main__':
1844 pass