blob: 7ab5b453b8e06f5fbebe84d06f7595133404d266 [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
4import gc
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05005import logging
Paul Vinciguerra72f00042018-11-25 11:05:13 -08006import sys
Ole Trøan162989e2018-11-26 10:27:50 +00007import os
8import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04009import signal
Paul Vinciguerrad6f22172020-12-05 22:39:14 +000010import subprocess
Ole Trøan162989e2018-11-26 10:27:50 +000011import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +020012import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020013import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080014import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000015import random
16import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080017import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010018import platform
Ole Trøan162989e2018-11-26 10:27:50 +000019from collections import deque
20from threading import Thread, Event
21from inspect import getdoc, isclass
22from traceback import format_exception
23from logging import FileHandler, DEBUG, Formatter
Andrew Yourtchenko06f32812021-01-14 10:19:08 +000024from enum import Enum
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070025
26import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000027from scapy.packet import Raw
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040028import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080029from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010030from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000031from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070032from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000033from vpp_papi_provider import VppPapiProvider
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050034import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000035from vpp_papi.vpp_stats import VPPStats
Paul Vinciguerra499ea642019-03-15 09:39:19 -070036from vpp_papi.vpp_transport_shmem import VppTransportShmemIOError
Ole Trøan162989e2018-11-26 10:27:50 +000037from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
38 get_logger, colorize
39from vpp_object import VppObjectRegistry
40from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020041from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
42from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
43from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080044
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050045logger = logging.getLogger(__name__)
46
47# Set up an empty logger for the testcase that can be overridden as necessary
48null_logger = logging.getLogger('VppTestCase')
49null_logger.addHandler(logging.NullHandler())
50
juraj.linkescae64f82018-09-19 15:01:47 +020051PASS = 0
52FAIL = 1
53ERROR = 2
54SKIP = 3
55TEST_RUN = 4
56
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040057
58class BoolEnvironmentVariable(object):
59
60 def __init__(self, env_var_name, default='n', true_values=None):
61 self.name = env_var_name
62 self.default = default
63 self.true_values = true_values if true_values is not None else \
64 ("y", "yes", "1")
65
66 def __bool__(self):
67 return os.getenv(self.name, self.default).lower() in self.true_values
68
69 if sys.version_info[0] == 2:
70 __nonzero__ = __bool__
71
72 def __repr__(self):
73 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
74 (self.name, self.default, self.true_values)
75
76
77debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
78if debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010079 import debug_internal
80
Klement Sekeraf62ae122016-10-11 11:47:09 +020081"""
82 Test framework module.
83
84 The module provides a set of tools for constructing and running tests and
85 representing the results.
86"""
87
Klement Sekeraf62ae122016-10-11 11:47:09 +020088
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040089class VppDiedError(Exception):
90 """ exception for reporting that the subprocess has died."""
91
92 signals_by_value = {v: k for k, v in signal.__dict__.items() if
93 k.startswith('SIG') and not k.startswith('SIG_')}
94
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040095 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040096 self.rv = rv
97 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040098 self.testcase = testcase
99 self.method_name = method_name
100
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400101 try:
102 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400103 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400104 pass
105
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400106 if testcase is None and method_name is None:
107 in_msg = ''
108 else:
109 in_msg = 'running %s.%s ' % (testcase, method_name)
110
111 msg = "VPP subprocess died %sunexpectedly with return code: %d%s." % (
112 in_msg,
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400113 self.rv,
Paul Vinciguerraf7457522019-07-13 09:35:38 -0400114 ' [%s]' % (self.signal_name if
115 self.signal_name is not None else ''))
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400116 super(VppDiedError, self).__init__(msg)
117
118
Damjan Marionf56b77a2016-10-03 19:44:57 +0200119class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200120 """Private class to create packet info object.
121
122 Help process information about the next packet.
123 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200124 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100125 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200126 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100127 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200128 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100129 #: Store the index of the destination packet generator interface
130 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200131 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100132 #: Store expected ip version
133 ip = -1
134 #: Store expected upper protocol
135 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100136 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200137 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200138
Matej Klotton16a14cd2016-12-07 15:09:13 +0100139 def __eq__(self, other):
140 index = self.index == other.index
141 src = self.src == other.src
142 dst = self.dst == other.dst
143 data = self.data == other.data
144 return index and src and dst and data
145
Klement Sekeraf62ae122016-10-11 11:47:09 +0200146
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100147def pump_output(testclass):
148 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100149 stdout_fragment = ""
150 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400151 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100152 readable = select.select([testclass.vpp.stdout.fileno(),
153 testclass.vpp.stderr.fileno(),
154 testclass.pump_thread_wakeup_pipe[0]],
155 [], [])[0]
156 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100157 read = os.read(testclass.vpp.stdout.fileno(), 102400)
158 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200159 split = read.decode('ascii',
160 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100161 if len(stdout_fragment) > 0:
162 split[0] = "%s%s" % (stdout_fragment, split[0])
163 if len(split) > 0 and split[-1].endswith("\n"):
164 limit = None
165 else:
166 limit = -1
167 stdout_fragment = split[-1]
168 testclass.vpp_stdout_deque.extend(split[:limit])
169 if not testclass.cache_vpp_output:
170 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100171 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100172 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100173 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100174 read = os.read(testclass.vpp.stderr.fileno(), 102400)
175 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200176 split = read.decode('ascii',
177 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100178 if len(stderr_fragment) > 0:
179 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200180 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100181 limit = None
182 else:
183 limit = -1
184 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200185
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100186 testclass.vpp_stderr_deque.extend(split[:limit])
187 if not testclass.cache_vpp_output:
188 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100189 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100190 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800191 # ignoring the dummy pipe here intentionally - the
192 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200193
194
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800195def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400196 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100197
Klement Sekera6aa58b72019-05-16 14:34:55 +0200198
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800199is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100200
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800201
202def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100203 return platform.machine() == 'aarch64'
204
Klement Sekera6aa58b72019-05-16 14:34:55 +0200205
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800206is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100207
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800208
209def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400210 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100211
Klement Sekera6aa58b72019-05-16 14:34:55 +0200212
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800213running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100214
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800215
Dave Barachd498c9e2020-03-10 16:59:39 -0400216def _running_gcov_tests():
217 return BoolEnvironmentVariable("GCOV_TESTS")
218
219
220running_gcov_tests = _running_gcov_tests()
221
222
Klement Sekera909a6a12017-08-08 04:33:53 +0200223class KeepAliveReporter(object):
224 """
225 Singleton object which reports test start to parent process
226 """
227 _shared_state = {}
228
229 def __init__(self):
230 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800231 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200232
233 @property
234 def pipe(self):
235 return self._pipe
236
237 @pipe.setter
238 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800239 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200240 raise Exception("Internal error - pipe should only be set once.")
241 self._pipe = pipe
242
juraj.linkes40dd73b2018-09-21 13:55:16 +0200243 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200244 """
245 Write current test tmpdir & desc to keep-alive pipe to signal liveness
246 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200247 if self.pipe is None:
248 # if not running forked..
249 return
250
Klement Sekera909a6a12017-08-08 04:33:53 +0200251 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200252 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200253 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200254 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200255
Dave Wallacee2efd122017-09-30 22:04:21 -0400256 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200257
258
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000259class TestCaseTag(Enum):
260 RUN_SOLO = 1
261
262
263def create_tag_decorator(e):
264 def decorator(cls):
265 try:
266 cls.test_tags.append(e)
267 except AttributeError:
268 cls.test_tags = [e]
269 return cls
270 return decorator
271
272tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
273
274
Damjan Marionf56b77a2016-10-03 19:44:57 +0200275class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100276 """This subclass is a base class for VPP test cases that are implemented as
277 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200278 """
279
Ole Troana45dc072018-12-21 16:04:22 +0100280 extra_vpp_punt_config = []
281 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500282 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400283 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100284
Klement Sekeraf62ae122016-10-11 11:47:09 +0200285 @property
286 def packet_infos(self):
287 """List of packet infos"""
288 return self._packet_infos
289
Klement Sekeradab231a2016-12-21 08:50:14 +0100290 @classmethod
291 def get_packet_count_for_if_idx(cls, dst_if_index):
292 """Get the number of packet info for specified destination if index"""
293 if dst_if_index in cls._packet_count_for_dst_if_idx:
294 return cls._packet_count_for_dst_if_idx[dst_if_index]
295 else:
296 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200297
298 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000299 def has_tag(cls, tag):
300 """ if the test case has a given tag - return true """
301 try:
302 return tag in cls.test_tags
303 except AttributeError:
304 pass
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +0000305 return False
306
307 @classmethod
Andrew Yourtchenko06f32812021-01-14 10:19:08 +0000308 def is_tagged_run_solo(cls):
309 """ if the test case class is timing-sensitive - return true """
310 return cls.has_tag(TestCaseTag.RUN_SOLO)
311
312 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200313 def instance(cls):
314 """Return the instance of this testcase"""
315 return cls.test_instance
316
Damjan Marionf56b77a2016-10-03 19:44:57 +0200317 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200318 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000319 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200320 cls.debug_core = False
321 cls.debug_gdb = False
322 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000323 cls.debug_all = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200324 if d is None:
325 return
326 dl = d.lower()
327 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200328 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000329 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200330 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000331 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200332 cls.debug_gdbserver = True
333 else:
334 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000335 if dl == "gdb-all" or dl == "gdbserver-all":
336 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200337
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800338 @staticmethod
339 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200340 cpu_usage_list = [set(range(psutil.cpu_count()))]
341 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
342 if 'vpp_main' == p.info['name']]
343 for vpp_process in vpp_processes:
344 for cpu_usage_set in cpu_usage_list:
345 try:
346 cpu_num = vpp_process.cpu_num()
347 if cpu_num in cpu_usage_set:
348 cpu_usage_set_index = cpu_usage_list.index(
349 cpu_usage_set)
350 if cpu_usage_set_index == len(cpu_usage_list) - 1:
351 cpu_usage_list.append({cpu_num})
352 else:
353 cpu_usage_list[cpu_usage_set_index + 1].add(
354 cpu_num)
355 cpu_usage_set.remove(cpu_num)
356 break
357 except psutil.NoSuchProcess:
358 pass
359
360 for cpu_usage_set in cpu_usage_list:
361 if len(cpu_usage_set) > 0:
362 min_usage_set = cpu_usage_set
363 break
364
365 return random.choice(tuple(min_usage_set))
366
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800367 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200368 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200369 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400370 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100371 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400372 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100373 c = os.getenv("CACHE_OUTPUT", "1")
374 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200375 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100376 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
377 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400378 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100379 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
380 plugin_path = None
381 if cls.plugin_path is not None:
382 if cls.extern_plugin_path is not None:
383 plugin_path = "%s:%s" % (
384 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100385 else:
386 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100387 elif cls.extern_plugin_path is not None:
388 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100389 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100390 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100391 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100392 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100393 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100394 if size is not None:
395 coredump_size = "coredump-size %s" % size
396 if coredump_size is None:
397 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200398
Ole Troana45dc072018-12-21 16:04:22 +0100399 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000400 if not hasattr(cls, "worker_config"):
401 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200402
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000403 default_variant = os.getenv("VARIANT")
404 if default_variant is not None:
405 default_variant = "defaults { %s 100 }" % default_variant
406 else:
407 default_variant = ""
408
Dave Barach77841402020-04-29 17:04:10 -0400409 api_fuzzing = os.getenv("API_FUZZ")
410 if api_fuzzing is None:
411 api_fuzzing = 'off'
412
Ole Troana45dc072018-12-21 16:04:22 +0100413 cls.vpp_cmdline = [cls.vpp_bin, "unix",
414 "{", "nodaemon", debug_cli, "full-coredump",
415 coredump_size, "runtime-dir", cls.tempdir, "}",
416 "api-trace", "{", "on", "}", "api-segment", "{",
417 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000418 "main-core", str(cpu_core_number),
419 cls.worker_config, "}",
Dave Barach4ed25982019-12-25 09:24:58 -0500420 "physmem", "{", "max-size", "32m", "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200421 "statseg", "{", "socket-name", cls.stats_sock, "}",
422 "socksvr", "{", "socket-name", cls.api_sock, "}",
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000423 "node { ", default_variant, "}",
Dave Barach77841402020-04-29 17:04:10 -0400424 "api-fuzz {", api_fuzzing, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200425 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100426 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200427 "}", "plugin", "rdma_plugin.so", "{", "disable",
Neale Ranns2b202bc2020-09-21 08:17:51 +0000428 "}", "plugin", "lisp_unittest_plugin.so", "{",
429 "enable",
Ole Troana45dc072018-12-21 16:04:22 +0100430 "}", "plugin", "unittest_plugin.so", "{", "enable",
431 "}"] + cls.extra_vpp_plugin_config + ["}", ]
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000432
Ole Troana45dc072018-12-21 16:04:22 +0100433 if cls.extra_vpp_punt_config is not None:
434 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100435 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100436 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400437 if cls.test_plugin_path is not None:
438 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
439
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100440 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
441 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200442
443 @classmethod
444 def wait_for_enter(cls):
445 if cls.debug_gdbserver:
446 print(double_line_delim)
447 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
448 elif cls.debug_gdb:
449 print(double_line_delim)
450 print("Spawned VPP with PID: %d" % cls.vpp.pid)
451 else:
452 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
453 return
454 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000455 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200456 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400457 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000458 " -ex 'target remote localhost:{port}'"
459 .format(port=cls.gdbserver_port))
460 print("Now is the time to attach gdb by running the above "
461 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200462 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000463 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200464 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400465 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000466 print("Now is the time to attach gdb by running the above "
467 "command and set up breakpoints etc., then resume VPP from"
468 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200469 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800470 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200471
472 @classmethod
473 def run_vpp(cls):
474 cmdline = cls.vpp_cmdline
475
476 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100477 gdbserver = '/usr/bin/gdbserver'
478 if not os.path.isfile(gdbserver) or \
479 not os.access(gdbserver, os.X_OK):
480 raise Exception("gdbserver binary '%s' does not exist or is "
481 "not executable" % gdbserver)
482
Dave Wallace24564332019-10-21 02:53:14 +0000483 cmdline = [gdbserver, 'localhost:{port}'
484 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200485 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
486
Klement Sekera931be3a2016-11-03 05:36:01 +0100487 try:
488 cls.vpp = subprocess.Popen(cmdline,
489 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100490 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800491 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800492 cls.logger.critical("Subprocess returned with non-0 return code: ("
493 "%s)", e.returncode)
494 raise
495 except OSError as e:
496 cls.logger.critical("Subprocess returned with OS error: "
497 "(%s) %s", e.errno, e.strerror)
498 raise
499 except Exception as e:
500 cls.logger.exception("Subprocess returned unexpected from "
501 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100502 raise
503
Klement Sekera277b89c2016-10-28 13:20:27 +0200504 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100505
Damjan Marionf56b77a2016-10-03 19:44:57 +0200506 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000507 def wait_for_coredump(cls):
508 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400509 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000510 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400511 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000512 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400513 ok = False
514 while time.time() < deadline:
515 cls.sleep(1)
516 size = curr_size
517 curr_size = os.path.getsize(corefile)
518 if size == curr_size:
519 ok = True
520 break
521 if not ok:
522 cls.logger.error("Timed out waiting for coredump to complete:"
523 " %s", corefile)
524 else:
525 cls.logger.error("Coredump complete: %s, size %d",
526 corefile, curr_size)
527
528 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200529 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200530 """
531 Perform class setup before running the testcase
532 Remove shared memory files, start vpp and connect the vpp-api
533 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800534 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100535 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100536 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000537 seed = os.environ["RND_SEED"]
538 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100539 if hasattr(cls, 'parallel_handler'):
540 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100541 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700542
Klement Sekeraf62ae122016-10-11 11:47:09 +0200543 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200544 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200545 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200546 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200547 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
548 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100549 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
550 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200551 cls.file_handler.setLevel(DEBUG)
552 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700553 cls.logger.debug("--- setUpClass() for %s called ---" %
554 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200555 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200556 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200557 cls.logger.info("Temporary dir is %s, shm prefix is %s",
558 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000559 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200560 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100561 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100562 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200563 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100564 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100565 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200566 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200567 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200568 # need to catch exceptions here because if we raise, then the cleanup
569 # doesn't get called and we might end with a zombie vpp
570 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200571 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200572 cls.reporter.send_keep_alive(cls, 'setUpClass')
573 VppTestResult.current_test_case_info = TestCaseInfo(
574 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100575 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100576 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100577 cls.pump_thread_stop_flag = Event()
578 cls.pump_thread_wakeup_pipe = os.pipe()
579 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100580 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100581 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200582 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400583 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200584 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400585 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100586 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400587 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100588 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400589 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100590 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200591 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200592 try:
593 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100594 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200595 cls.vpp_startup_failed = True
596 cls.logger.critical(
597 "VPP died shortly after startup, check the"
598 " output to standard error for possible cause")
599 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100600 try:
601 cls.vapi.connect()
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500602 except vpp_papi.VPPIOError as e:
603 cls.logger.debug("Exception connecting to vapi: %s" % e)
604 cls.vapi.disconnect()
605
Klement Sekera085f5c02016-11-24 01:59:16 +0100606 if cls.debug_gdbserver:
607 print(colorize("You're running VPP inside gdbserver but "
608 "VPP-API connection failed, did you forget "
609 "to 'continue' VPP from within gdb?", RED))
610 raise
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400611 except vpp_papi.VPPRuntimeError as e:
612 cls.logger.debug("%s" % e)
613 cls.quit()
614 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000615 except Exception as e:
616 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400617 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100618 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200619
Damjan Marionf56b77a2016-10-03 19:44:57 +0200620 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500621 def _debug_quit(cls):
622 if (cls.debug_gdbserver or cls.debug_gdb):
623 try:
624 cls.vpp.poll()
625
626 if cls.vpp.returncode is None:
627 print()
628 print(double_line_delim)
629 print("VPP or GDB server is still running")
630 print(single_line_delim)
631 input("When done debugging, press ENTER to kill the "
632 "process and finish running the testcase...")
633 except AttributeError:
634 pass
635
636 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200637 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200638 """
639 Disconnect vpp-api, kill vpp and cleanup shared memory files
640 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500641 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200642
juraj.linkes184870a2018-07-16 14:22:01 +0200643 # first signal that we want to stop the pump thread, then wake it up
644 if hasattr(cls, 'pump_thread_stop_flag'):
645 cls.pump_thread_stop_flag.set()
646 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100647 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100648 if hasattr(cls, 'pump_thread'):
649 cls.logger.debug("Waiting for pump thread to stop")
650 cls.pump_thread.join()
651 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500652 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100653 cls.vpp_stderr_reader_thread.join()
654
Klement Sekeraf62ae122016-10-11 11:47:09 +0200655 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100656 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100657 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700658 cls.logger.debug("Disconnecting class vapi client on %s",
659 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100660 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700661 cls.logger.debug("Deleting class vapi attribute on %s",
662 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100663 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200664 cls.vpp.poll()
665 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000666 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100667 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400668 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100669 cls.logger.debug("Waiting for vpp to die")
670 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700671 cls.logger.debug("Deleting class vpp attribute on %s",
672 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200673 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200674
Klement Sekera3747c752017-04-10 06:30:17 +0200675 if cls.vpp_startup_failed:
676 stdout_log = cls.logger.info
677 stderr_log = cls.logger.critical
678 else:
679 stdout_log = cls.logger.info
680 stderr_log = cls.logger.info
681
Klement Sekerae4504c62016-12-08 10:16:41 +0100682 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200683 stdout_log(single_line_delim)
684 stdout_log('VPP output to stdout while running %s:', cls.__name__)
685 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100686 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200687 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
688 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200689 stdout_log('\n%s', vpp_output)
690 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200691
Klement Sekerae4504c62016-12-08 10:16:41 +0100692 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200693 stderr_log(single_line_delim)
694 stderr_log('VPP output to stderr while running %s:', cls.__name__)
695 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100696 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200697 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
698 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200699 stderr_log('\n%s', vpp_output)
700 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200701
Damjan Marionf56b77a2016-10-03 19:44:57 +0200702 @classmethod
703 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200704 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700705 cls.logger.debug("--- tearDownClass() for %s called ---" %
706 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200707 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200708 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200709 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100710 cls.reset_packet_infos()
711 if debug_framework:
712 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200713
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700714 def show_commands_at_teardown(self):
715 """ Allow subclass specific teardown logging additions."""
716 self.logger.info("--- No test specific show commands provided. ---")
717
Damjan Marionf56b77a2016-10-03 19:44:57 +0200718 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200719 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100720 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
721 (self.__class__.__name__, self._testMethodName,
722 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700723
724 try:
725 if not self.vpp_dead:
726 self.logger.debug(self.vapi.cli("show trace max 1000"))
727 self.logger.info(self.vapi.ppcli("show interface"))
728 self.logger.info(self.vapi.ppcli("show hardware"))
729 self.logger.info(self.statistics.set_errors_str())
730 self.logger.info(self.vapi.ppcli("show run"))
731 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400732 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700733 self.logger.info("Logging testcase specific show commands.")
734 self.show_commands_at_teardown()
735 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500736 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000737 m = self._testMethodName
738 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500739 tmp_api_trace = "/tmp/%s" % api_trace
740 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
741 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
742 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
743 vpp_api_trace_log))
744 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500745 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500746 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700747 except VppTransportShmemIOError:
748 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
749 "Cannot log show commands.")
750 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100751 else:
752 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200753
Damjan Marionf56b77a2016-10-03 19:44:57 +0200754 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200755 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800756 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200757 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100758 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400759
760 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
761 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100762 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100763 self.vpp_stdout_deque.append(
764 "--- test setUp() for %s.%s(%s) starts here ---\n" %
765 (self.__class__.__name__, self._testMethodName,
766 self._testMethodDoc))
767 self.vpp_stderr_deque.append(
768 "--- test setUp() for %s.%s(%s) starts here ---\n" %
769 (self.__class__.__name__, self._testMethodName,
770 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200771 self.vapi.cli("clear trace")
772 # store the test instance inside the test class - so that objects
773 # holding the class can access instance methods (like assertEqual)
774 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200775
Damjan Marionf56b77a2016-10-03 19:44:57 +0200776 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200777 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200778 """
779 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200780
Klement Sekera75e7d132017-09-20 08:26:30 +0200781 :param interfaces: iterable interface indexes (if None,
782 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200783
Klement Sekeraf62ae122016-10-11 11:47:09 +0200784 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200785 if interfaces is None:
786 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200787 for i in interfaces:
788 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200789
Damjan Marionf56b77a2016-10-03 19:44:57 +0200790 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100791 def register_capture(cls, cap_name):
792 """ Register a capture in the testclass """
793 # add to the list of captures with current timestamp
794 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100795
796 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000797 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400798 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
799 # returns float("2.190522")
800 timestr = cls.vapi.cli('show clock')
801 head, sep, tail = timestr.partition(',')
802 head, sep, tail = head.partition('Time now')
803 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000804
805 @classmethod
806 def sleep_on_vpp_time(cls, sec):
807 """ Sleep according to time in VPP world """
808 # On a busy system with many processes
809 # we might end up with VPP time being slower than real world
810 # So take that into account when waiting for VPP to do something
811 start_time = cls.get_vpp_time()
812 while cls.get_vpp_time() - start_time < sec:
813 cls.sleep(0.1)
814
815 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100816 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000817 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100818 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200819 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000820 # PG, when starts, runs to completion -
821 # so let's avoid a race condition,
822 # and wait a little till it's done.
823 # Then clean it up - and then be gone.
824 deadline = time.time() + 300
825 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
826 cls.sleep(0.01) # yield
827 if time.time() > deadline:
828 cls.logger.error("Timeout waiting for pg to stop")
829 break
830 for stamp, cap_name in cls._captures:
831 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100832 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200833
Damjan Marionf56b77a2016-10-03 19:44:57 +0200834 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200835 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200836 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100837 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200838
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100839 :param interfaces: iterable indexes of the interfaces.
840 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200841
Klement Sekeraf62ae122016-10-11 11:47:09 +0200842 """
843 result = []
844 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200845 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200846 setattr(cls, intf.name, intf)
847 result.append(intf)
848 cls.pg_interfaces = result
849 return result
850
Matej Klotton0178d522016-11-04 11:11:44 +0100851 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200852 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100853 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100854 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100855
Klement Sekerab9ef2732018-06-24 22:49:33 +0200856 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100857 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100858 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200859 result = [VppLoInterface(cls) for i in range(count)]
860 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100861 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100862 cls.lo_interfaces = result
863 return result
864
Neale Ranns192b13f2019-03-15 02:16:20 -0700865 @classmethod
866 def create_bvi_interfaces(cls, count):
867 """
868 Create BVI interfaces.
869
870 :param count: number of interfaces created.
871 :returns: List of created interfaces.
872 """
873 result = [VppBviInterface(cls) for i in range(count)]
874 for intf in result:
875 setattr(cls, intf.name, intf)
876 cls.bvi_interfaces = result
877 return result
878
Damjan Marionf56b77a2016-10-03 19:44:57 +0200879 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200880 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200881 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200882 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200883 NOTE: Currently works only when Raw layer is present.
884
885 :param packet: packet
886 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200887 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200888
889 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200890 packet_len = len(packet) + 4
891 extend = size - packet_len
892 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200893 num = (extend // len(padding)) + 1
894 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200895
Klement Sekeradab231a2016-12-21 08:50:14 +0100896 @classmethod
897 def reset_packet_infos(cls):
898 """ Reset the list of packet info objects and packet counts to zero """
899 cls._packet_infos = {}
900 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200901
Klement Sekeradab231a2016-12-21 08:50:14 +0100902 @classmethod
903 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200904 """
905 Create packet info object containing the source and destination indexes
906 and add it to the testcase's packet info list
907
Klement Sekeradab231a2016-12-21 08:50:14 +0100908 :param VppInterface src_if: source interface
909 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200910
911 :returns: _PacketInfo object
912
913 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200914 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100915 info.index = len(cls._packet_infos)
916 info.src = src_if.sw_if_index
917 info.dst = dst_if.sw_if_index
918 if isinstance(dst_if, VppSubInterface):
919 dst_idx = dst_if.parent.sw_if_index
920 else:
921 dst_idx = dst_if.sw_if_index
922 if dst_idx in cls._packet_count_for_dst_if_idx:
923 cls._packet_count_for_dst_if_idx[dst_idx] += 1
924 else:
925 cls._packet_count_for_dst_if_idx[dst_idx] = 1
926 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200927 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200928
Damjan Marionf56b77a2016-10-03 19:44:57 +0200929 @staticmethod
930 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200931 """
932 Convert _PacketInfo object to packet payload
933
934 :param info: _PacketInfo object
935
936 :returns: string containing serialized data from packet info
937 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100938 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
939 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200940
Damjan Marionf56b77a2016-10-03 19:44:57 +0200941 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800942 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200943 """
944 Convert packet payload to _PacketInfo object
945
946 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700947 :type payload: <class 'scapy.packet.Raw'>
948 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800949 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700950 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200951 :returns: _PacketInfo object containing de-serialized data from payload
952
953 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800954 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200955 info = _PacketInfo()
956 info.index = int(numbers[0])
957 info.src = int(numbers[1])
958 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100959 info.ip = int(numbers[3])
960 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200961 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200962
Damjan Marionf56b77a2016-10-03 19:44:57 +0200963 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200964 """
965 Iterate over the packet info list stored in the testcase
966 Start iteration with first element if info is None
967 Continue based on index in info if info is specified
968
969 :param info: info or None
970 :returns: next info in list or None if no more infos
971 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200972 if info is None:
973 next_index = 0
974 else:
975 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100976 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200977 return None
978 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100979 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200980
Klement Sekeraf62ae122016-10-11 11:47:09 +0200981 def get_next_packet_info_for_interface(self, src_index, info):
982 """
983 Search the packet info list for the next packet info with same source
984 interface index
985
986 :param src_index: source interface index to search for
987 :param info: packet info - where to start the search
988 :returns: packet info or None
989
990 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200991 while True:
992 info = self.get_next_packet_info(info)
993 if info is None:
994 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200995 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200996 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200997
Klement Sekeraf62ae122016-10-11 11:47:09 +0200998 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
999 """
1000 Search the packet info list for the next packet info with same source
1001 and destination interface indexes
1002
1003 :param src_index: source interface index to search for
1004 :param dst_index: destination interface index to search for
1005 :param info: packet info - where to start the search
1006 :returns: packet info or None
1007
1008 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001009 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001010 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001011 if info is None:
1012 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001013 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001014 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001015
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001016 def assert_equal(self, real_value, expected_value, name_or_class=None):
1017 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001018 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001019 return
1020 try:
1021 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1022 msg = msg % (getdoc(name_or_class).strip(),
1023 real_value, str(name_or_class(real_value)),
1024 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001025 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001026 msg = "Invalid %s: %s does not match expected value %s" % (
1027 name_or_class, real_value, expected_value)
1028
1029 self.assertEqual(real_value, expected_value, msg)
1030
Klement Sekerab17dd962017-01-09 07:43:48 +01001031 def assert_in_range(self,
1032 real_value,
1033 expected_min,
1034 expected_max,
1035 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001036 if name is None:
1037 msg = None
1038 else:
1039 msg = "Invalid %s: %s out of range <%s,%s>" % (
1040 name, real_value, expected_min, expected_max)
1041 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1042
Klement Sekerad81ae412018-05-16 10:52:54 +02001043 def assert_packet_checksums_valid(self, packet,
1044 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001045 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001046 udp_layers = ['UDP', 'UDPerror']
1047 checksum_fields = ['cksum', 'chksum']
1048 checksums = []
1049 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001050 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001051 while True:
1052 layer = temp.getlayer(counter)
1053 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001054 layer = layer.copy()
1055 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001056 for cf in checksum_fields:
1057 if hasattr(layer, cf):
1058 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001059 0 == getattr(layer, cf) and \
1060 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001061 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001062 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001063 checksums.append((counter, cf))
1064 else:
1065 break
1066 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001067 if 0 == len(checksums):
1068 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001069 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001070 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001071 calc_sum = getattr(temp[layer], cf)
1072 self.assert_equal(
1073 getattr(received[layer], cf), calc_sum,
1074 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1075 self.logger.debug(
1076 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1077 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001078
1079 def assert_checksum_valid(self, received_packet, layer,
1080 field_name='chksum',
1081 ignore_zero_checksum=False):
1082 """ Check checksum of received packet on given layer """
1083 received_packet_checksum = getattr(received_packet[layer], field_name)
1084 if ignore_zero_checksum and 0 == received_packet_checksum:
1085 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001086 recalculated = received_packet.__class__(
1087 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001088 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001089 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001090 self.assert_equal(received_packet_checksum,
1091 getattr(recalculated[layer], field_name),
1092 "packet checksum on layer: %s" % layer)
1093
1094 def assert_ip_checksum_valid(self, received_packet,
1095 ignore_zero_checksum=False):
1096 self.assert_checksum_valid(received_packet, 'IP',
1097 ignore_zero_checksum=ignore_zero_checksum)
1098
1099 def assert_tcp_checksum_valid(self, received_packet,
1100 ignore_zero_checksum=False):
1101 self.assert_checksum_valid(received_packet, 'TCP',
1102 ignore_zero_checksum=ignore_zero_checksum)
1103
1104 def assert_udp_checksum_valid(self, received_packet,
1105 ignore_zero_checksum=True):
1106 self.assert_checksum_valid(received_packet, 'UDP',
1107 ignore_zero_checksum=ignore_zero_checksum)
1108
1109 def assert_embedded_icmp_checksum_valid(self, received_packet):
1110 if received_packet.haslayer(IPerror):
1111 self.assert_checksum_valid(received_packet, 'IPerror')
1112 if received_packet.haslayer(TCPerror):
1113 self.assert_checksum_valid(received_packet, 'TCPerror')
1114 if received_packet.haslayer(UDPerror):
1115 self.assert_checksum_valid(received_packet, 'UDPerror',
1116 ignore_zero_checksum=True)
1117 if received_packet.haslayer(ICMPerror):
1118 self.assert_checksum_valid(received_packet, 'ICMPerror')
1119
1120 def assert_icmp_checksum_valid(self, received_packet):
1121 self.assert_checksum_valid(received_packet, 'ICMP')
1122 self.assert_embedded_icmp_checksum_valid(received_packet)
1123
1124 def assert_icmpv6_checksum_valid(self, pkt):
1125 if pkt.haslayer(ICMPv6DestUnreach):
1126 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1127 self.assert_embedded_icmp_checksum_valid(pkt)
1128 if pkt.haslayer(ICMPv6EchoRequest):
1129 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1130 if pkt.haslayer(ICMPv6EchoReply):
1131 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1132
Klement Sekera3a343d42019-05-16 14:35:46 +02001133 def get_packet_counter(self, counter):
1134 if counter.startswith("/"):
1135 counter_value = self.statistics.get_counter(counter)
1136 else:
1137 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001138 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001139 for i in range(1, len(counters) - 1):
1140 results = counters[i].split()
1141 if results[1] == counter:
1142 counter_value = int(results[0])
1143 break
1144 return counter_value
1145
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001146 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001147 counter_value = self.get_packet_counter(counter)
1148 self.assert_equal(counter_value, expected_value,
1149 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001150
Ole Troan233e4682019-05-16 15:01:34 +02001151 def assert_error_counter_equal(self, counter, expected_value):
1152 counter_value = self.statistics.get_err_counter(counter)
1153 self.assert_equal(counter_value, expected_value,
1154 "error counter `%s'" % counter)
1155
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001156 @classmethod
1157 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001158
1159 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1160 # * by Guido, only the main thread can be interrupted.
1161 # */
1162 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1163 if timeout == 0:
1164 # yield quantum
1165 if hasattr(os, 'sched_yield'):
1166 os.sched_yield()
1167 else:
1168 time.sleep(0)
1169 return
1170
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001171 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001172 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001173 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001174 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001175 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001176 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001177 "slept for %es instead of ~%es!",
1178 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001179
1180 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001181 "Finished sleep (%s) - slept %es (wanted %es)",
1182 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001183
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001184 def pg_send(self, intf, pkts, worker=None):
Neale Ranns52fae862018-01-08 04:41:42 -08001185 self.vapi.cli("clear trace")
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001186 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001187 self.pg_enable_capture(self.pg_interfaces)
1188 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001189
1190 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1191 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001192 if not timeout:
1193 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001194 for i in self.pg_interfaces:
1195 i.get_capture(0, timeout=timeout)
1196 i.assert_nothing_captured(remark=remark)
1197 timeout = 0.1
1198
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001199 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None):
Neale Rannsd7603d92019-03-28 08:56:10 +00001200 if not n_rx:
1201 n_rx = len(pkts)
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001202 self.pg_send(intf, pkts, worker=worker)
Neale Rannsd7603d92019-03-28 08:56:10 +00001203 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001204 return rx
1205
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001206 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1207 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001208 rx = output.get_capture(len(pkts))
1209 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001210 if not timeout:
1211 timeout = 1
1212 for i in self.pg_interfaces:
1213 if i not in outputs:
1214 i.get_capture(0, timeout=timeout)
1215 i.assert_nothing_captured()
1216 timeout = 0.1
1217
Neale Ranns52fae862018-01-08 04:41:42 -08001218 return rx
1219
Damjan Marionf56b77a2016-10-03 19:44:57 +02001220
juraj.linkes184870a2018-07-16 14:22:01 +02001221def get_testcase_doc_name(test):
1222 return getdoc(test.__class__).splitlines()[0]
1223
1224
Ole Trøan5ba91592018-11-22 10:01:09 +00001225def get_test_description(descriptions, test):
1226 short_description = test.shortDescription()
1227 if descriptions and short_description:
1228 return short_description
1229 else:
1230 return str(test)
1231
1232
juraj.linkes40dd73b2018-09-21 13:55:16 +02001233class TestCaseInfo(object):
1234 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1235 self.logger = logger
1236 self.tempdir = tempdir
1237 self.vpp_pid = vpp_pid
1238 self.vpp_bin_path = vpp_bin_path
1239 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001240
1241
Damjan Marionf56b77a2016-10-03 19:44:57 +02001242class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001243 """
1244 @property result_string
1245 String variable to store the test case result string.
1246 @property errors
1247 List variable containing 2-tuples of TestCase instances and strings
1248 holding formatted tracebacks. Each tuple represents a test which
1249 raised an unexpected exception.
1250 @property failures
1251 List variable containing 2-tuples of TestCase instances and strings
1252 holding formatted tracebacks. Each tuple represents a test where
1253 a failure was explicitly signalled using the TestCase.assert*()
1254 methods.
1255 """
1256
juraj.linkes40dd73b2018-09-21 13:55:16 +02001257 failed_test_cases_info = set()
1258 core_crash_test_cases_info = set()
1259 current_test_case_info = None
1260
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001261 def __init__(self, stream=None, descriptions=None, verbosity=None,
1262 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001263 """
Klement Sekerada505f62017-01-04 12:58:53 +01001264 :param stream File descriptor to store where to report test results.
1265 Set to the standard error stream by default.
1266 :param descriptions Boolean variable to store information if to use
1267 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001268 :param verbosity Integer variable to store required verbosity level.
1269 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001270 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001271 self.stream = stream
1272 self.descriptions = descriptions
1273 self.verbosity = verbosity
1274 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001275 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001276
Damjan Marionf56b77a2016-10-03 19:44:57 +02001277 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001278 """
1279 Record a test succeeded result
1280
1281 :param test:
1282
1283 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001284 if self.current_test_case_info:
1285 self.current_test_case_info.logger.debug(
1286 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1287 test._testMethodName,
1288 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001289 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001290 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001291
juraj.linkescae64f82018-09-19 15:01:47 +02001292 self.send_result_through_pipe(test, PASS)
1293
Klement Sekeraf62ae122016-10-11 11:47:09 +02001294 def addSkip(self, test, reason):
1295 """
1296 Record a test skipped.
1297
1298 :param test:
1299 :param reason:
1300
1301 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001302 if self.current_test_case_info:
1303 self.current_test_case_info.logger.debug(
1304 "--- addSkip() %s.%s(%s) called, reason is %s" %
1305 (test.__class__.__name__, test._testMethodName,
1306 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001307 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001308 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001309
juraj.linkescae64f82018-09-19 15:01:47 +02001310 self.send_result_through_pipe(test, SKIP)
1311
juraj.linkes40dd73b2018-09-21 13:55:16 +02001312 def symlink_failed(self):
1313 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001314 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001315 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001316 link_path = os.path.join(
1317 failed_dir,
1318 '%s-FAILED' %
1319 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001320
1321 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001322 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001323 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001324 "os.symlink(%s, %s)" %
1325 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001326 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001327 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001328 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001329 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001330 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001331
Klement Sekeraf413bef2017-08-15 07:09:02 +02001332 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001333 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001334
juraj.linkescae64f82018-09-19 15:01:47 +02001335 def send_result_through_pipe(self, test, result):
1336 if hasattr(self, 'test_framework_result_pipe'):
1337 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001338 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001339 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001340
juraj.linkes40dd73b2018-09-21 13:55:16 +02001341 def log_error(self, test, err, fn_name):
1342 if self.current_test_case_info:
1343 if isinstance(test, unittest.suite._ErrorHolder):
1344 test_name = test.description
1345 else:
1346 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1347 test._testMethodName,
1348 test._testMethodDoc)
1349 self.current_test_case_info.logger.debug(
1350 "--- %s() %s called, err is %s" %
1351 (fn_name, test_name, err))
1352 self.current_test_case_info.logger.debug(
1353 "formatted exception is:\n%s" %
1354 "".join(format_exception(*err)))
1355
1356 def add_error(self, test, err, unittest_fn, error_type):
1357 if error_type == FAIL:
1358 self.log_error(test, err, 'addFailure')
1359 error_type_str = colorize("FAIL", RED)
1360 elif error_type == ERROR:
1361 self.log_error(test, err, 'addError')
1362 error_type_str = colorize("ERROR", RED)
1363 else:
1364 raise Exception('Error type %s cannot be used to record an '
1365 'error or a failure' % error_type)
1366
1367 unittest_fn(self, test, err)
1368 if self.current_test_case_info:
1369 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1370 (error_type_str,
1371 self.current_test_case_info.tempdir)
1372 self.symlink_failed()
1373 self.failed_test_cases_info.add(self.current_test_case_info)
1374 if is_core_present(self.current_test_case_info.tempdir):
1375 if not self.current_test_case_info.core_crash_test:
1376 if isinstance(test, unittest.suite._ErrorHolder):
1377 test_name = str(test)
1378 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001379 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001380 get_testcase_doc_name(test), test.id())
1381 self.current_test_case_info.core_crash_test = test_name
1382 self.core_crash_test_cases_info.add(
1383 self.current_test_case_info)
1384 else:
1385 self.result_string = '%s [no temp dir]' % error_type_str
1386
1387 self.send_result_through_pipe(test, error_type)
1388
Damjan Marionf56b77a2016-10-03 19:44:57 +02001389 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001390 """
1391 Record a test failed result
1392
1393 :param test:
1394 :param err: error message
1395
1396 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001397 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001398
Damjan Marionf56b77a2016-10-03 19:44:57 +02001399 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001400 """
1401 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001402
Klement Sekeraf62ae122016-10-11 11:47:09 +02001403 :param test:
1404 :param err: error message
1405
1406 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001407 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001408
Damjan Marionf56b77a2016-10-03 19:44:57 +02001409 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001410 """
1411 Get test description
1412
1413 :param test:
1414 :returns: test description
1415
1416 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001417 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001418
Damjan Marionf56b77a2016-10-03 19:44:57 +02001419 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001420 """
1421 Start a test
1422
1423 :param test:
1424
1425 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001426
1427 def print_header(test):
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001428 test_doc = getdoc(test)
1429 if not test_doc:
1430 raise Exception("No doc string for test '%s'" % test.id())
1431 test_title = test_doc.splitlines()[0]
1432 test_title_colored = colorize(test_title, GREEN)
Andrew Yourtchenko06f32812021-01-14 10:19:08 +00001433 if test.is_tagged_run_solo():
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001434 # long live PEP-8 and 80 char width limitation...
1435 c = YELLOW
1436 test_title_colored = colorize("SOLO RUN: " + test_title, c)
1437
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001438 if not hasattr(test.__class__, '_header_printed'):
1439 print(double_line_delim)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001440 print(test_title_colored)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001441 print(double_line_delim)
1442 test.__class__._header_printed = True
1443
1444 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001445 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001446 unittest.TestResult.startTest(self, test)
1447 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001448 self.stream.writeln(
1449 "Starting " + self.getDescription(test) + " ...")
1450 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001451
Damjan Marionf56b77a2016-10-03 19:44:57 +02001452 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001453 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001454 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001455
1456 :param test:
1457
1458 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001459 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001460
Damjan Marionf56b77a2016-10-03 19:44:57 +02001461 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001462 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001463 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001464 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001465 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001466 else:
Ole Troan0c629322019-11-28 14:48:44 +01001467 self.stream.writeln("%-68s %4.2f %s" %
1468 (self.getDescription(test),
1469 time.time() - self.start_test,
1470 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001471
1472 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001473
Damjan Marionf56b77a2016-10-03 19:44:57 +02001474 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001475 """
1476 Print errors from running the test case
1477 """
juraj.linkesabec0122018-11-16 17:28:56 +01001478 if len(self.errors) > 0 or len(self.failures) > 0:
1479 self.stream.writeln()
1480 self.printErrorList('ERROR', self.errors)
1481 self.printErrorList('FAIL', self.failures)
1482
1483 # ^^ that is the last output from unittest before summary
1484 if not self.runner.print_summary:
1485 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1486 self.stream = devnull
1487 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001488
Damjan Marionf56b77a2016-10-03 19:44:57 +02001489 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001490 """
1491 Print error list to the output stream together with error type
1492 and test case description.
1493
1494 :param flavour: error type
1495 :param errors: iterable errors
1496
1497 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001498 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001499 self.stream.writeln(double_line_delim)
1500 self.stream.writeln("%s: %s" %
1501 (flavour, self.getDescription(test)))
1502 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001503 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001504
1505
Damjan Marionf56b77a2016-10-03 19:44:57 +02001506class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001507 """
Klement Sekera104543f2017-02-03 07:29:43 +01001508 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001509 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001510
Klement Sekeraf62ae122016-10-11 11:47:09 +02001511 @property
1512 def resultclass(self):
1513 """Class maintaining the results of the tests"""
1514 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001515
juraj.linkes184870a2018-07-16 14:22:01 +02001516 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001517 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001518 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001519 # ignore stream setting here, use hard-coded stdout to be in sync
1520 # with prints from VppTestCase methods ...
1521 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1522 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001523 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001524 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001525
juraj.linkesabec0122018-11-16 17:28:56 +01001526 self.orig_stream = self.stream
1527 self.resultclass.test_framework_result_pipe = result_pipe
1528
1529 self.print_summary = print_summary
1530
1531 def _makeResult(self):
1532 return self.resultclass(self.stream,
1533 self.descriptions,
1534 self.verbosity,
1535 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001536
Damjan Marionf56b77a2016-10-03 19:44:57 +02001537 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001538 """
1539 Run the tests
1540
1541 :param test:
1542
1543 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001544 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001545
1546 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001547 if not self.print_summary:
1548 self.stream = self.orig_stream
1549 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001550 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001551
1552
1553class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001554 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1555 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001556 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001557 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001558 if hasattr(self, 'testcase') and self.testcase.debug_all:
1559 if self.testcase.debug_gdbserver:
1560 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1561 .format(port=self.testcase.gdbserver_port)] + args
1562 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1563 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001564 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001565 self.app_name = os.path.basename(self.app_bin)
1566 if hasattr(self, 'role'):
1567 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001568 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001569 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001570 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001571 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001572
Dave Wallace24564332019-10-21 02:53:14 +00001573 def wait_for_enter(self):
1574 if not hasattr(self, 'testcase'):
1575 return
1576 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1577 print()
1578 print(double_line_delim)
1579 print("Spawned GDB Server for '{app}' with PID: {pid}"
1580 .format(app=self.app_name, pid=self.process.pid))
1581 elif self.testcase.debug_all and self.testcase.debug_gdb:
1582 print()
1583 print(double_line_delim)
1584 print("Spawned '{app}' with PID: {pid}"
1585 .format(app=self.app_name, pid=self.process.pid))
1586 else:
1587 return
1588 print(single_line_delim)
1589 print("You can debug '{app}' using:".format(app=self.app_name))
1590 if self.testcase.debug_gdbserver:
1591 print("sudo gdb " + self.app_bin +
1592 " -ex 'target remote localhost:{port}'"
1593 .format(port=self.testcase.gdbserver_port))
1594 print("Now is the time to attach gdb by running the above "
1595 "command, set up breakpoints etc., then resume from "
1596 "within gdb by issuing the 'continue' command")
1597 self.testcase.gdbserver_port += 1
1598 elif self.testcase.debug_gdb:
1599 print("sudo gdb " + self.app_bin +
1600 " -ex 'attach {pid}'".format(pid=self.process.pid))
1601 print("Now is the time to attach gdb by running the above "
1602 "command and set up breakpoints etc., then resume from"
1603 " within gdb by issuing the 'continue' command")
1604 print(single_line_delim)
1605 input("Press ENTER to continue running the testcase...")
1606
Neale Ranns812ed392017-10-16 04:20:13 -07001607 def run(self):
1608 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001609 if not os.path.exists(executable) or not os.access(
1610 executable, os.F_OK | os.X_OK):
1611 # Exit code that means some system file did not exist,
1612 # could not be opened, or had some other kind of error.
1613 self.result = os.EX_OSFILE
1614 raise EnvironmentError(
1615 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001616 self.logger.debug("Running executable: '{app}'"
1617 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001618 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001619 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001620 env["CK_LOG_FILE_NAME"] = "-"
1621 self.process = subprocess.Popen(
1622 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1623 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001624 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001625 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001626 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001627 self.logger.info("Return code is `%s'" % self.process.returncode)
1628 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001629 self.logger.info("Executable `{app}' wrote to stdout:"
1630 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001631 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001632 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001633 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001634 self.logger.info("Executable `{app}' wrote to stderr:"
1635 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001636 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001637 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001638 self.logger.info(single_line_delim)
1639 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001640
Klement Sekera6aa58b72019-05-16 14:34:55 +02001641
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001642if __name__ == '__main__':
1643 pass