blob: 82d1c35fd7c66aedd3500c97c34fb9325b1086b3 [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
Ole Trøan162989e2018-11-26 10:27:50 +000010import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +020011import tempfile
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
Paul Vinciguerra72f00042018-11-25 11:05:13 -080016import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010017import platform
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
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070023
24import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000025from scapy.packet import Raw
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040026import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080027from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010028from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000029from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070030from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000031from vpp_papi_provider import VppPapiProvider
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050032import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000033from vpp_papi.vpp_stats import VPPStats
Paul Vinciguerra499ea642019-03-15 09:39:19 -070034from vpp_papi.vpp_transport_shmem import VppTransportShmemIOError
Ole Trøan162989e2018-11-26 10:27:50 +000035from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
36 get_logger, colorize
37from vpp_object import VppObjectRegistry
38from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020039from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
40from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
41from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080042
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010043if os.name == 'posix' and sys.version_info[0] < 3:
44 # using subprocess32 is recommended by python official documentation
45 # @ https://docs.python.org/2/library/subprocess.html
46 import subprocess32 as subprocess
47else:
48 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020049
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080050# Python2/3 compatible
51try:
52 input = raw_input
53except NameError:
54 pass
55
Paul Vinciguerra9beabd82019-12-01 22:24:28 -050056logger = logging.getLogger(__name__)
57
58# Set up an empty logger for the testcase that can be overridden as necessary
59null_logger = logging.getLogger('VppTestCase')
60null_logger.addHandler(logging.NullHandler())
61
juraj.linkescae64f82018-09-19 15:01:47 +020062PASS = 0
63FAIL = 1
64ERROR = 2
65SKIP = 3
66TEST_RUN = 4
67
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040068
69class BoolEnvironmentVariable(object):
70
71 def __init__(self, env_var_name, default='n', true_values=None):
72 self.name = env_var_name
73 self.default = default
74 self.true_values = true_values if true_values is not None else \
75 ("y", "yes", "1")
76
77 def __bool__(self):
78 return os.getenv(self.name, self.default).lower() in self.true_values
79
80 if sys.version_info[0] == 2:
81 __nonzero__ = __bool__
82
83 def __repr__(self):
84 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
85 (self.name, self.default, self.true_values)
86
87
88debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
89if debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010090 import debug_internal
91
Klement Sekeraf62ae122016-10-11 11:47:09 +020092"""
93 Test framework module.
94
95 The module provides a set of tools for constructing and running tests and
96 representing the results.
97"""
98
Klement Sekeraf62ae122016-10-11 11:47:09 +020099
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400100class VppDiedError(Exception):
101 """ exception for reporting that the subprocess has died."""
102
103 signals_by_value = {v: k for k, v in signal.__dict__.items() if
104 k.startswith('SIG') and not k.startswith('SIG_')}
105
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400106 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400107 self.rv = rv
108 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400109 self.testcase = testcase
110 self.method_name = method_name
111
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400112 try:
113 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400114 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400115 pass
116
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400117 if testcase is None and method_name is None:
118 in_msg = ''
119 else:
120 in_msg = 'running %s.%s ' % (testcase, method_name)
121
122 msg = "VPP subprocess died %sunexpectedly with return code: %d%s." % (
123 in_msg,
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400124 self.rv,
Paul Vinciguerraf7457522019-07-13 09:35:38 -0400125 ' [%s]' % (self.signal_name if
126 self.signal_name is not None else ''))
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400127 super(VppDiedError, self).__init__(msg)
128
129
Damjan Marionf56b77a2016-10-03 19:44:57 +0200130class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200131 """Private class to create packet info object.
132
133 Help process information about the next packet.
134 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200135 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100136 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200137 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100138 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200139 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100140 #: Store the index of the destination packet generator interface
141 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200142 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100143 #: Store expected ip version
144 ip = -1
145 #: Store expected upper protocol
146 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100147 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200148 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200149
Matej Klotton16a14cd2016-12-07 15:09:13 +0100150 def __eq__(self, other):
151 index = self.index == other.index
152 src = self.src == other.src
153 dst = self.dst == other.dst
154 data = self.data == other.data
155 return index and src and dst and data
156
Klement Sekeraf62ae122016-10-11 11:47:09 +0200157
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100158def pump_output(testclass):
159 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100160 stdout_fragment = ""
161 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400162 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100163 readable = select.select([testclass.vpp.stdout.fileno(),
164 testclass.vpp.stderr.fileno(),
165 testclass.pump_thread_wakeup_pipe[0]],
166 [], [])[0]
167 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100168 read = os.read(testclass.vpp.stdout.fileno(), 102400)
169 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200170 split = read.decode('ascii',
171 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100172 if len(stdout_fragment) > 0:
173 split[0] = "%s%s" % (stdout_fragment, split[0])
174 if len(split) > 0 and split[-1].endswith("\n"):
175 limit = None
176 else:
177 limit = -1
178 stdout_fragment = split[-1]
179 testclass.vpp_stdout_deque.extend(split[:limit])
180 if not testclass.cache_vpp_output:
181 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100182 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100183 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100184 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100185 read = os.read(testclass.vpp.stderr.fileno(), 102400)
186 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200187 split = read.decode('ascii',
188 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100189 if len(stderr_fragment) > 0:
190 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200191 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100192 limit = None
193 else:
194 limit = -1
195 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200196
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100197 testclass.vpp_stderr_deque.extend(split[:limit])
198 if not testclass.cache_vpp_output:
199 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100200 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100201 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800202 # ignoring the dummy pipe here intentionally - the
203 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200204
205
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800206def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400207 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100208
Klement Sekera6aa58b72019-05-16 14:34:55 +0200209
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800210is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100211
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800212
213def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100214 return platform.machine() == 'aarch64'
215
Klement Sekera6aa58b72019-05-16 14:34:55 +0200216
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800217is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100218
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800219
220def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400221 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100222
Klement Sekera6aa58b72019-05-16 14:34:55 +0200223
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800224running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100225
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800226
Dave Barachd498c9e2020-03-10 16:59:39 -0400227def _running_gcov_tests():
228 return BoolEnvironmentVariable("GCOV_TESTS")
229
230
231running_gcov_tests = _running_gcov_tests()
232
233
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800234def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100235 os_id = os.getenv("OS_ID", "")
236 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200237
Klement Sekera6aa58b72019-05-16 14:34:55 +0200238
Klement Sekera3a350702019-09-02 14:26:26 +0000239running_on_centos = _running_on_centos()
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800240
Klement Sekerad3e671e2017-09-29 12:36:37 +0200241
Klement Sekera909a6a12017-08-08 04:33:53 +0200242class KeepAliveReporter(object):
243 """
244 Singleton object which reports test start to parent process
245 """
246 _shared_state = {}
247
248 def __init__(self):
249 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800250 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200251
252 @property
253 def pipe(self):
254 return self._pipe
255
256 @pipe.setter
257 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800258 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200259 raise Exception("Internal error - pipe should only be set once.")
260 self._pipe = pipe
261
juraj.linkes40dd73b2018-09-21 13:55:16 +0200262 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200263 """
264 Write current test tmpdir & desc to keep-alive pipe to signal liveness
265 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200266 if self.pipe is None:
267 # if not running forked..
268 return
269
Klement Sekera909a6a12017-08-08 04:33:53 +0200270 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200271 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200272 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200273 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200274
Dave Wallacee2efd122017-09-30 22:04:21 -0400275 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200276
277
Damjan Marionf56b77a2016-10-03 19:44:57 +0200278class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100279 """This subclass is a base class for VPP test cases that are implemented as
280 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200281 """
282
Ole Troana45dc072018-12-21 16:04:22 +0100283 extra_vpp_punt_config = []
284 extra_vpp_plugin_config = []
Paul Vinciguerra9beabd82019-12-01 22:24:28 -0500285 logger = null_logger
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400286 vapi_response_timeout = 5
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 Yourtchenkoa3b7c552020-08-26 14:33:54 +0000302 def force_solo(cls):
303 """ if the test case class is timing-sensitive - return true """
304 return False
305
306 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200307 def instance(cls):
308 """Return the instance of this testcase"""
309 return cls.test_instance
310
Damjan Marionf56b77a2016-10-03 19:44:57 +0200311 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200312 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000313 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200314 cls.debug_core = False
315 cls.debug_gdb = False
316 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000317 cls.debug_all = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200318 if d is None:
319 return
320 dl = d.lower()
321 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200322 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000323 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200324 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000325 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200326 cls.debug_gdbserver = True
327 else:
328 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000329 if dl == "gdb-all" or dl == "gdbserver-all":
330 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200331
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800332 @staticmethod
333 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200334 cpu_usage_list = [set(range(psutil.cpu_count()))]
335 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
336 if 'vpp_main' == p.info['name']]
337 for vpp_process in vpp_processes:
338 for cpu_usage_set in cpu_usage_list:
339 try:
340 cpu_num = vpp_process.cpu_num()
341 if cpu_num in cpu_usage_set:
342 cpu_usage_set_index = cpu_usage_list.index(
343 cpu_usage_set)
344 if cpu_usage_set_index == len(cpu_usage_list) - 1:
345 cpu_usage_list.append({cpu_num})
346 else:
347 cpu_usage_list[cpu_usage_set_index + 1].add(
348 cpu_num)
349 cpu_usage_set.remove(cpu_num)
350 break
351 except psutil.NoSuchProcess:
352 pass
353
354 for cpu_usage_set in cpu_usage_list:
355 if len(cpu_usage_set) > 0:
356 min_usage_set = cpu_usage_set
357 break
358
359 return random.choice(tuple(min_usage_set))
360
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800361 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200362 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200363 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400364 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100365 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400366 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100367 c = os.getenv("CACHE_OUTPUT", "1")
368 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200369 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100370 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
371 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400372 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100373 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
374 plugin_path = None
375 if cls.plugin_path is not None:
376 if cls.extern_plugin_path is not None:
377 plugin_path = "%s:%s" % (
378 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100379 else:
380 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100381 elif cls.extern_plugin_path is not None:
382 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100383 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100384 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100385 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100386 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100387 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100388 if size is not None:
389 coredump_size = "coredump-size %s" % size
390 if coredump_size is None:
391 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200392
Ole Troana45dc072018-12-21 16:04:22 +0100393 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000394 if not hasattr(cls, "worker_config"):
395 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200396
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000397 default_variant = os.getenv("VARIANT")
398 if default_variant is not None:
399 default_variant = "defaults { %s 100 }" % default_variant
400 else:
401 default_variant = ""
402
Dave Barach77841402020-04-29 17:04:10 -0400403 api_fuzzing = os.getenv("API_FUZZ")
404 if api_fuzzing is None:
405 api_fuzzing = 'off'
406
Ole Troana45dc072018-12-21 16:04:22 +0100407 cls.vpp_cmdline = [cls.vpp_bin, "unix",
408 "{", "nodaemon", debug_cli, "full-coredump",
409 coredump_size, "runtime-dir", cls.tempdir, "}",
410 "api-trace", "{", "on", "}", "api-segment", "{",
411 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000412 "main-core", str(cpu_core_number),
413 cls.worker_config, "}",
Dave Barach4ed25982019-12-25 09:24:58 -0500414 "physmem", "{", "max-size", "32m", "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200415 "statseg", "{", "socket-name", cls.stats_sock, "}",
416 "socksvr", "{", "socket-name", cls.api_sock, "}",
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000417 "node { ", default_variant, "}",
Dave Barach77841402020-04-29 17:04:10 -0400418 "api-fuzz {", api_fuzzing, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200419 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100420 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200421 "}", "plugin", "rdma_plugin.so", "{", "disable",
Neale Ranns2b202bc2020-09-21 08:17:51 +0000422 "}", "plugin", "lisp_unittest_plugin.so", "{",
423 "enable",
Ole Troana45dc072018-12-21 16:04:22 +0100424 "}", "plugin", "unittest_plugin.so", "{", "enable",
425 "}"] + cls.extra_vpp_plugin_config + ["}", ]
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000426
Ole Troana45dc072018-12-21 16:04:22 +0100427 if cls.extra_vpp_punt_config is not None:
428 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100429 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100430 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400431 if cls.test_plugin_path is not None:
432 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
433
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100434 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
435 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200436
437 @classmethod
438 def wait_for_enter(cls):
439 if cls.debug_gdbserver:
440 print(double_line_delim)
441 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
442 elif cls.debug_gdb:
443 print(double_line_delim)
444 print("Spawned VPP with PID: %d" % cls.vpp.pid)
445 else:
446 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
447 return
448 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000449 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200450 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400451 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000452 " -ex 'target remote localhost:{port}'"
453 .format(port=cls.gdbserver_port))
454 print("Now is the time to attach gdb by running the above "
455 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200456 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000457 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200458 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400459 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000460 print("Now is the time to attach gdb by running the above "
461 "command and set up breakpoints etc., then resume VPP from"
462 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200463 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800464 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200465
466 @classmethod
467 def run_vpp(cls):
468 cmdline = cls.vpp_cmdline
469
470 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100471 gdbserver = '/usr/bin/gdbserver'
472 if not os.path.isfile(gdbserver) or \
473 not os.access(gdbserver, os.X_OK):
474 raise Exception("gdbserver binary '%s' does not exist or is "
475 "not executable" % gdbserver)
476
Dave Wallace24564332019-10-21 02:53:14 +0000477 cmdline = [gdbserver, 'localhost:{port}'
478 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200479 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
480
Klement Sekera931be3a2016-11-03 05:36:01 +0100481 try:
482 cls.vpp = subprocess.Popen(cmdline,
483 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100484 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800485 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800486 cls.logger.critical("Subprocess returned with non-0 return code: ("
487 "%s)", e.returncode)
488 raise
489 except OSError as e:
490 cls.logger.critical("Subprocess returned with OS error: "
491 "(%s) %s", e.errno, e.strerror)
492 raise
493 except Exception as e:
494 cls.logger.exception("Subprocess returned unexpected from "
495 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100496 raise
497
Klement Sekera277b89c2016-10-28 13:20:27 +0200498 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100499
Damjan Marionf56b77a2016-10-03 19:44:57 +0200500 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000501 def wait_for_coredump(cls):
502 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400503 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000504 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400505 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000506 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400507 ok = False
508 while time.time() < deadline:
509 cls.sleep(1)
510 size = curr_size
511 curr_size = os.path.getsize(corefile)
512 if size == curr_size:
513 ok = True
514 break
515 if not ok:
516 cls.logger.error("Timed out waiting for coredump to complete:"
517 " %s", corefile)
518 else:
519 cls.logger.error("Coredump complete: %s, size %d",
520 corefile, curr_size)
521
522 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200523 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200524 """
525 Perform class setup before running the testcase
526 Remove shared memory files, start vpp and connect the vpp-api
527 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800528 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100529 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100530 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000531 seed = os.environ["RND_SEED"]
532 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100533 if hasattr(cls, 'parallel_handler'):
534 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100535 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700536
Klement Sekeraf62ae122016-10-11 11:47:09 +0200537 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200538 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200539 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200540 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200541 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
542 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100543 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
544 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200545 cls.file_handler.setLevel(DEBUG)
546 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700547 cls.logger.debug("--- setUpClass() for %s called ---" %
548 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200549 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200550 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200551 cls.logger.info("Temporary dir is %s, shm prefix is %s",
552 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000553 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200554 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100555 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100556 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200557 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100558 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100559 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200560 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200561 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200562 # need to catch exceptions here because if we raise, then the cleanup
563 # doesn't get called and we might end with a zombie vpp
564 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200565 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200566 cls.reporter.send_keep_alive(cls, 'setUpClass')
567 VppTestResult.current_test_case_info = TestCaseInfo(
568 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100569 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100570 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100571 cls.pump_thread_stop_flag = Event()
572 cls.pump_thread_wakeup_pipe = os.pipe()
573 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100574 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100575 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200576 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400577 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200578 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400579 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100580 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400581 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100582 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400583 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100584 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200585 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200586 try:
587 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100588 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200589 cls.vpp_startup_failed = True
590 cls.logger.critical(
591 "VPP died shortly after startup, check the"
592 " output to standard error for possible cause")
593 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100594 try:
595 cls.vapi.connect()
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500596 except vpp_papi.VPPIOError as e:
597 cls.logger.debug("Exception connecting to vapi: %s" % e)
598 cls.vapi.disconnect()
599
Klement Sekera085f5c02016-11-24 01:59:16 +0100600 if cls.debug_gdbserver:
601 print(colorize("You're running VPP inside gdbserver but "
602 "VPP-API connection failed, did you forget "
603 "to 'continue' VPP from within gdb?", RED))
604 raise
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400605 except vpp_papi.VPPRuntimeError as e:
606 cls.logger.debug("%s" % e)
607 cls.quit()
608 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000609 except Exception as e:
610 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400611 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100612 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200613
Damjan Marionf56b77a2016-10-03 19:44:57 +0200614 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500615 def _debug_quit(cls):
616 if (cls.debug_gdbserver or cls.debug_gdb):
617 try:
618 cls.vpp.poll()
619
620 if cls.vpp.returncode is None:
621 print()
622 print(double_line_delim)
623 print("VPP or GDB server is still running")
624 print(single_line_delim)
625 input("When done debugging, press ENTER to kill the "
626 "process and finish running the testcase...")
627 except AttributeError:
628 pass
629
630 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200631 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200632 """
633 Disconnect vpp-api, kill vpp and cleanup shared memory files
634 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500635 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200636
juraj.linkes184870a2018-07-16 14:22:01 +0200637 # first signal that we want to stop the pump thread, then wake it up
638 if hasattr(cls, 'pump_thread_stop_flag'):
639 cls.pump_thread_stop_flag.set()
640 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100641 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100642 if hasattr(cls, 'pump_thread'):
643 cls.logger.debug("Waiting for pump thread to stop")
644 cls.pump_thread.join()
645 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500646 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100647 cls.vpp_stderr_reader_thread.join()
648
Klement Sekeraf62ae122016-10-11 11:47:09 +0200649 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100650 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100651 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700652 cls.logger.debug("Disconnecting class vapi client on %s",
653 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100654 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700655 cls.logger.debug("Deleting class vapi attribute on %s",
656 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100657 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200658 cls.vpp.poll()
659 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000660 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100661 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400662 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100663 cls.logger.debug("Waiting for vpp to die")
664 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700665 cls.logger.debug("Deleting class vpp attribute on %s",
666 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200667 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200668
Klement Sekera3747c752017-04-10 06:30:17 +0200669 if cls.vpp_startup_failed:
670 stdout_log = cls.logger.info
671 stderr_log = cls.logger.critical
672 else:
673 stdout_log = cls.logger.info
674 stderr_log = cls.logger.info
675
Klement Sekerae4504c62016-12-08 10:16:41 +0100676 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200677 stdout_log(single_line_delim)
678 stdout_log('VPP output to stdout while running %s:', cls.__name__)
679 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100680 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200681 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
682 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200683 stdout_log('\n%s', vpp_output)
684 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200685
Klement Sekerae4504c62016-12-08 10:16:41 +0100686 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200687 stderr_log(single_line_delim)
688 stderr_log('VPP output to stderr while running %s:', cls.__name__)
689 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100690 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200691 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
692 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200693 stderr_log('\n%s', vpp_output)
694 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200695
Damjan Marionf56b77a2016-10-03 19:44:57 +0200696 @classmethod
697 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200698 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700699 cls.logger.debug("--- tearDownClass() for %s called ---" %
700 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200701 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200702 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200703 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100704 cls.reset_packet_infos()
705 if debug_framework:
706 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200707
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700708 def show_commands_at_teardown(self):
709 """ Allow subclass specific teardown logging additions."""
710 self.logger.info("--- No test specific show commands provided. ---")
711
Damjan Marionf56b77a2016-10-03 19:44:57 +0200712 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200713 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100714 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
715 (self.__class__.__name__, self._testMethodName,
716 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700717
718 try:
719 if not self.vpp_dead:
720 self.logger.debug(self.vapi.cli("show trace max 1000"))
721 self.logger.info(self.vapi.ppcli("show interface"))
722 self.logger.info(self.vapi.ppcli("show hardware"))
723 self.logger.info(self.statistics.set_errors_str())
724 self.logger.info(self.vapi.ppcli("show run"))
725 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400726 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700727 self.logger.info("Logging testcase specific show commands.")
728 self.show_commands_at_teardown()
729 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500730 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000731 m = self._testMethodName
732 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500733 tmp_api_trace = "/tmp/%s" % api_trace
734 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
735 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
736 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
737 vpp_api_trace_log))
738 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500739 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500740 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700741 except VppTransportShmemIOError:
742 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
743 "Cannot log show commands.")
744 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100745 else:
746 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200747
Damjan Marionf56b77a2016-10-03 19:44:57 +0200748 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200749 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800750 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200751 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100752 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400753
754 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
755 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100756 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100757 self.vpp_stdout_deque.append(
758 "--- test setUp() for %s.%s(%s) starts here ---\n" %
759 (self.__class__.__name__, self._testMethodName,
760 self._testMethodDoc))
761 self.vpp_stderr_deque.append(
762 "--- test setUp() for %s.%s(%s) starts here ---\n" %
763 (self.__class__.__name__, self._testMethodName,
764 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200765 self.vapi.cli("clear trace")
766 # store the test instance inside the test class - so that objects
767 # holding the class can access instance methods (like assertEqual)
768 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769
Damjan Marionf56b77a2016-10-03 19:44:57 +0200770 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200771 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200772 """
773 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200774
Klement Sekera75e7d132017-09-20 08:26:30 +0200775 :param interfaces: iterable interface indexes (if None,
776 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200777
Klement Sekeraf62ae122016-10-11 11:47:09 +0200778 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200779 if interfaces is None:
780 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200781 for i in interfaces:
782 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200783
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100785 def register_capture(cls, cap_name):
786 """ Register a capture in the testclass """
787 # add to the list of captures with current timestamp
788 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100789
790 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000791 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400792 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
793 # returns float("2.190522")
794 timestr = cls.vapi.cli('show clock')
795 head, sep, tail = timestr.partition(',')
796 head, sep, tail = head.partition('Time now')
797 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000798
799 @classmethod
800 def sleep_on_vpp_time(cls, sec):
801 """ Sleep according to time in VPP world """
802 # On a busy system with many processes
803 # we might end up with VPP time being slower than real world
804 # So take that into account when waiting for VPP to do something
805 start_time = cls.get_vpp_time()
806 while cls.get_vpp_time() - start_time < sec:
807 cls.sleep(0.1)
808
809 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100810 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000811 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100812 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200813 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000814 # PG, when starts, runs to completion -
815 # so let's avoid a race condition,
816 # and wait a little till it's done.
817 # Then clean it up - and then be gone.
818 deadline = time.time() + 300
819 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
820 cls.sleep(0.01) # yield
821 if time.time() > deadline:
822 cls.logger.error("Timeout waiting for pg to stop")
823 break
824 for stamp, cap_name in cls._captures:
825 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100826 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200827
Damjan Marionf56b77a2016-10-03 19:44:57 +0200828 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200829 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200830 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100831 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200832
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100833 :param interfaces: iterable indexes of the interfaces.
834 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200835
Klement Sekeraf62ae122016-10-11 11:47:09 +0200836 """
837 result = []
838 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200839 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200840 setattr(cls, intf.name, intf)
841 result.append(intf)
842 cls.pg_interfaces = result
843 return result
844
Matej Klotton0178d522016-11-04 11:11:44 +0100845 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200846 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100847 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100848 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100849
Klement Sekerab9ef2732018-06-24 22:49:33 +0200850 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100851 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100852 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200853 result = [VppLoInterface(cls) for i in range(count)]
854 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100855 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100856 cls.lo_interfaces = result
857 return result
858
Neale Ranns192b13f2019-03-15 02:16:20 -0700859 @classmethod
860 def create_bvi_interfaces(cls, count):
861 """
862 Create BVI interfaces.
863
864 :param count: number of interfaces created.
865 :returns: List of created interfaces.
866 """
867 result = [VppBviInterface(cls) for i in range(count)]
868 for intf in result:
869 setattr(cls, intf.name, intf)
870 cls.bvi_interfaces = result
871 return result
872
Damjan Marionf56b77a2016-10-03 19:44:57 +0200873 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200874 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200875 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200876 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200877 NOTE: Currently works only when Raw layer is present.
878
879 :param packet: packet
880 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200881 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200882
883 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200884 packet_len = len(packet) + 4
885 extend = size - packet_len
886 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200887 num = (extend // len(padding)) + 1
888 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200889
Klement Sekeradab231a2016-12-21 08:50:14 +0100890 @classmethod
891 def reset_packet_infos(cls):
892 """ Reset the list of packet info objects and packet counts to zero """
893 cls._packet_infos = {}
894 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200895
Klement Sekeradab231a2016-12-21 08:50:14 +0100896 @classmethod
897 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200898 """
899 Create packet info object containing the source and destination indexes
900 and add it to the testcase's packet info list
901
Klement Sekeradab231a2016-12-21 08:50:14 +0100902 :param VppInterface src_if: source interface
903 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200904
905 :returns: _PacketInfo object
906
907 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200908 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100909 info.index = len(cls._packet_infos)
910 info.src = src_if.sw_if_index
911 info.dst = dst_if.sw_if_index
912 if isinstance(dst_if, VppSubInterface):
913 dst_idx = dst_if.parent.sw_if_index
914 else:
915 dst_idx = dst_if.sw_if_index
916 if dst_idx in cls._packet_count_for_dst_if_idx:
917 cls._packet_count_for_dst_if_idx[dst_idx] += 1
918 else:
919 cls._packet_count_for_dst_if_idx[dst_idx] = 1
920 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200921 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200922
Damjan Marionf56b77a2016-10-03 19:44:57 +0200923 @staticmethod
924 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200925 """
926 Convert _PacketInfo object to packet payload
927
928 :param info: _PacketInfo object
929
930 :returns: string containing serialized data from packet info
931 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100932 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
933 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200934
Damjan Marionf56b77a2016-10-03 19:44:57 +0200935 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800936 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200937 """
938 Convert packet payload to _PacketInfo object
939
940 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700941 :type payload: <class 'scapy.packet.Raw'>
942 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800943 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700944 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200945 :returns: _PacketInfo object containing de-serialized data from payload
946
947 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800948 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200949 info = _PacketInfo()
950 info.index = int(numbers[0])
951 info.src = int(numbers[1])
952 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100953 info.ip = int(numbers[3])
954 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200955 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200956
Damjan Marionf56b77a2016-10-03 19:44:57 +0200957 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200958 """
959 Iterate over the packet info list stored in the testcase
960 Start iteration with first element if info is None
961 Continue based on index in info if info is specified
962
963 :param info: info or None
964 :returns: next info in list or None if no more infos
965 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200966 if info is None:
967 next_index = 0
968 else:
969 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100970 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200971 return None
972 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100973 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200974
Klement Sekeraf62ae122016-10-11 11:47:09 +0200975 def get_next_packet_info_for_interface(self, src_index, info):
976 """
977 Search the packet info list for the next packet info with same source
978 interface index
979
980 :param src_index: source interface index to search for
981 :param info: packet info - where to start the search
982 :returns: packet info or None
983
984 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200985 while True:
986 info = self.get_next_packet_info(info)
987 if info is None:
988 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200989 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200990 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200991
Klement Sekeraf62ae122016-10-11 11:47:09 +0200992 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
993 """
994 Search the packet info list for the next packet info with same source
995 and destination interface indexes
996
997 :param src_index: source interface index to search for
998 :param dst_index: destination interface index to search for
999 :param info: packet info - where to start the search
1000 :returns: packet info or None
1001
1002 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001003 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001004 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001005 if info is None:
1006 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001007 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001008 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001009
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001010 def assert_equal(self, real_value, expected_value, name_or_class=None):
1011 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001012 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001013 return
1014 try:
1015 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1016 msg = msg % (getdoc(name_or_class).strip(),
1017 real_value, str(name_or_class(real_value)),
1018 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001019 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001020 msg = "Invalid %s: %s does not match expected value %s" % (
1021 name_or_class, real_value, expected_value)
1022
1023 self.assertEqual(real_value, expected_value, msg)
1024
Klement Sekerab17dd962017-01-09 07:43:48 +01001025 def assert_in_range(self,
1026 real_value,
1027 expected_min,
1028 expected_max,
1029 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001030 if name is None:
1031 msg = None
1032 else:
1033 msg = "Invalid %s: %s out of range <%s,%s>" % (
1034 name, real_value, expected_min, expected_max)
1035 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1036
Klement Sekerad81ae412018-05-16 10:52:54 +02001037 def assert_packet_checksums_valid(self, packet,
1038 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001039 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001040 udp_layers = ['UDP', 'UDPerror']
1041 checksum_fields = ['cksum', 'chksum']
1042 checksums = []
1043 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001044 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001045 while True:
1046 layer = temp.getlayer(counter)
1047 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001048 layer = layer.copy()
1049 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001050 for cf in checksum_fields:
1051 if hasattr(layer, cf):
1052 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001053 0 == getattr(layer, cf) and \
1054 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001055 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001056 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001057 checksums.append((counter, cf))
1058 else:
1059 break
1060 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001061 if 0 == len(checksums):
1062 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001063 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001064 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001065 calc_sum = getattr(temp[layer], cf)
1066 self.assert_equal(
1067 getattr(received[layer], cf), calc_sum,
1068 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1069 self.logger.debug(
1070 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1071 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001072
1073 def assert_checksum_valid(self, received_packet, layer,
1074 field_name='chksum',
1075 ignore_zero_checksum=False):
1076 """ Check checksum of received packet on given layer """
1077 received_packet_checksum = getattr(received_packet[layer], field_name)
1078 if ignore_zero_checksum and 0 == received_packet_checksum:
1079 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001080 recalculated = received_packet.__class__(
1081 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001082 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001083 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001084 self.assert_equal(received_packet_checksum,
1085 getattr(recalculated[layer], field_name),
1086 "packet checksum on layer: %s" % layer)
1087
1088 def assert_ip_checksum_valid(self, received_packet,
1089 ignore_zero_checksum=False):
1090 self.assert_checksum_valid(received_packet, 'IP',
1091 ignore_zero_checksum=ignore_zero_checksum)
1092
1093 def assert_tcp_checksum_valid(self, received_packet,
1094 ignore_zero_checksum=False):
1095 self.assert_checksum_valid(received_packet, 'TCP',
1096 ignore_zero_checksum=ignore_zero_checksum)
1097
1098 def assert_udp_checksum_valid(self, received_packet,
1099 ignore_zero_checksum=True):
1100 self.assert_checksum_valid(received_packet, 'UDP',
1101 ignore_zero_checksum=ignore_zero_checksum)
1102
1103 def assert_embedded_icmp_checksum_valid(self, received_packet):
1104 if received_packet.haslayer(IPerror):
1105 self.assert_checksum_valid(received_packet, 'IPerror')
1106 if received_packet.haslayer(TCPerror):
1107 self.assert_checksum_valid(received_packet, 'TCPerror')
1108 if received_packet.haslayer(UDPerror):
1109 self.assert_checksum_valid(received_packet, 'UDPerror',
1110 ignore_zero_checksum=True)
1111 if received_packet.haslayer(ICMPerror):
1112 self.assert_checksum_valid(received_packet, 'ICMPerror')
1113
1114 def assert_icmp_checksum_valid(self, received_packet):
1115 self.assert_checksum_valid(received_packet, 'ICMP')
1116 self.assert_embedded_icmp_checksum_valid(received_packet)
1117
1118 def assert_icmpv6_checksum_valid(self, pkt):
1119 if pkt.haslayer(ICMPv6DestUnreach):
1120 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1121 self.assert_embedded_icmp_checksum_valid(pkt)
1122 if pkt.haslayer(ICMPv6EchoRequest):
1123 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1124 if pkt.haslayer(ICMPv6EchoReply):
1125 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1126
Klement Sekera3a343d42019-05-16 14:35:46 +02001127 def get_packet_counter(self, counter):
1128 if counter.startswith("/"):
1129 counter_value = self.statistics.get_counter(counter)
1130 else:
1131 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001132 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001133 for i in range(1, len(counters) - 1):
1134 results = counters[i].split()
1135 if results[1] == counter:
1136 counter_value = int(results[0])
1137 break
1138 return counter_value
1139
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001140 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001141 counter_value = self.get_packet_counter(counter)
1142 self.assert_equal(counter_value, expected_value,
1143 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001144
Ole Troan233e4682019-05-16 15:01:34 +02001145 def assert_error_counter_equal(self, counter, expected_value):
1146 counter_value = self.statistics.get_err_counter(counter)
1147 self.assert_equal(counter_value, expected_value,
1148 "error counter `%s'" % counter)
1149
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001150 @classmethod
1151 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001152
1153 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1154 # * by Guido, only the main thread can be interrupted.
1155 # */
1156 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1157 if timeout == 0:
1158 # yield quantum
1159 if hasattr(os, 'sched_yield'):
1160 os.sched_yield()
1161 else:
1162 time.sleep(0)
1163 return
1164
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001165 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001166 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001167 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001168 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001169 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001170 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001171 "slept for %es instead of ~%es!",
1172 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001173
1174 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001175 "Finished sleep (%s) - slept %es (wanted %es)",
1176 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001177
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001178 def pg_send(self, intf, pkts, worker=None):
Neale Ranns52fae862018-01-08 04:41:42 -08001179 self.vapi.cli("clear trace")
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001180 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001181 self.pg_enable_capture(self.pg_interfaces)
1182 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001183
1184 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1185 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001186 if not timeout:
1187 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001188 for i in self.pg_interfaces:
1189 i.get_capture(0, timeout=timeout)
1190 i.assert_nothing_captured(remark=remark)
1191 timeout = 0.1
1192
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001193 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None):
Neale Rannsd7603d92019-03-28 08:56:10 +00001194 if not n_rx:
1195 n_rx = len(pkts)
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001196 self.pg_send(intf, pkts, worker=worker)
Neale Rannsd7603d92019-03-28 08:56:10 +00001197 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001198 return rx
1199
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001200 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1201 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001202 rx = output.get_capture(len(pkts))
1203 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001204 if not timeout:
1205 timeout = 1
1206 for i in self.pg_interfaces:
1207 if i not in outputs:
1208 i.get_capture(0, timeout=timeout)
1209 i.assert_nothing_captured()
1210 timeout = 0.1
1211
Neale Ranns52fae862018-01-08 04:41:42 -08001212 return rx
1213
Damjan Marionf56b77a2016-10-03 19:44:57 +02001214
juraj.linkes184870a2018-07-16 14:22:01 +02001215def get_testcase_doc_name(test):
1216 return getdoc(test.__class__).splitlines()[0]
1217
1218
Ole Trøan5ba91592018-11-22 10:01:09 +00001219def get_test_description(descriptions, test):
1220 short_description = test.shortDescription()
1221 if descriptions and short_description:
1222 return short_description
1223 else:
1224 return str(test)
1225
1226
juraj.linkes40dd73b2018-09-21 13:55:16 +02001227class TestCaseInfo(object):
1228 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1229 self.logger = logger
1230 self.tempdir = tempdir
1231 self.vpp_pid = vpp_pid
1232 self.vpp_bin_path = vpp_bin_path
1233 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001234
1235
Damjan Marionf56b77a2016-10-03 19:44:57 +02001236class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001237 """
1238 @property result_string
1239 String variable to store the test case result string.
1240 @property errors
1241 List variable containing 2-tuples of TestCase instances and strings
1242 holding formatted tracebacks. Each tuple represents a test which
1243 raised an unexpected exception.
1244 @property failures
1245 List variable containing 2-tuples of TestCase instances and strings
1246 holding formatted tracebacks. Each tuple represents a test where
1247 a failure was explicitly signalled using the TestCase.assert*()
1248 methods.
1249 """
1250
juraj.linkes40dd73b2018-09-21 13:55:16 +02001251 failed_test_cases_info = set()
1252 core_crash_test_cases_info = set()
1253 current_test_case_info = None
1254
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001255 def __init__(self, stream=None, descriptions=None, verbosity=None,
1256 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001257 """
Klement Sekerada505f62017-01-04 12:58:53 +01001258 :param stream File descriptor to store where to report test results.
1259 Set to the standard error stream by default.
1260 :param descriptions Boolean variable to store information if to use
1261 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001262 :param verbosity Integer variable to store required verbosity level.
1263 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001264 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001265 self.stream = stream
1266 self.descriptions = descriptions
1267 self.verbosity = verbosity
1268 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001269 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001270
Damjan Marionf56b77a2016-10-03 19:44:57 +02001271 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001272 """
1273 Record a test succeeded result
1274
1275 :param test:
1276
1277 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001278 if self.current_test_case_info:
1279 self.current_test_case_info.logger.debug(
1280 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1281 test._testMethodName,
1282 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001283 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001284 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001285
juraj.linkescae64f82018-09-19 15:01:47 +02001286 self.send_result_through_pipe(test, PASS)
1287
Klement Sekeraf62ae122016-10-11 11:47:09 +02001288 def addSkip(self, test, reason):
1289 """
1290 Record a test skipped.
1291
1292 :param test:
1293 :param reason:
1294
1295 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001296 if self.current_test_case_info:
1297 self.current_test_case_info.logger.debug(
1298 "--- addSkip() %s.%s(%s) called, reason is %s" %
1299 (test.__class__.__name__, test._testMethodName,
1300 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001301 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001302 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001303
juraj.linkescae64f82018-09-19 15:01:47 +02001304 self.send_result_through_pipe(test, SKIP)
1305
juraj.linkes40dd73b2018-09-21 13:55:16 +02001306 def symlink_failed(self):
1307 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001308 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001309 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001310 link_path = os.path.join(
1311 failed_dir,
1312 '%s-FAILED' %
1313 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001314
1315 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001316 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001317 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001318 "os.symlink(%s, %s)" %
1319 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001320 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001321 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001322 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001323 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001324 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001325
Klement Sekeraf413bef2017-08-15 07:09:02 +02001326 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001327 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001328
juraj.linkescae64f82018-09-19 15:01:47 +02001329 def send_result_through_pipe(self, test, result):
1330 if hasattr(self, 'test_framework_result_pipe'):
1331 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001332 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001333 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001334
juraj.linkes40dd73b2018-09-21 13:55:16 +02001335 def log_error(self, test, err, fn_name):
1336 if self.current_test_case_info:
1337 if isinstance(test, unittest.suite._ErrorHolder):
1338 test_name = test.description
1339 else:
1340 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1341 test._testMethodName,
1342 test._testMethodDoc)
1343 self.current_test_case_info.logger.debug(
1344 "--- %s() %s called, err is %s" %
1345 (fn_name, test_name, err))
1346 self.current_test_case_info.logger.debug(
1347 "formatted exception is:\n%s" %
1348 "".join(format_exception(*err)))
1349
1350 def add_error(self, test, err, unittest_fn, error_type):
1351 if error_type == FAIL:
1352 self.log_error(test, err, 'addFailure')
1353 error_type_str = colorize("FAIL", RED)
1354 elif error_type == ERROR:
1355 self.log_error(test, err, 'addError')
1356 error_type_str = colorize("ERROR", RED)
1357 else:
1358 raise Exception('Error type %s cannot be used to record an '
1359 'error or a failure' % error_type)
1360
1361 unittest_fn(self, test, err)
1362 if self.current_test_case_info:
1363 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1364 (error_type_str,
1365 self.current_test_case_info.tempdir)
1366 self.symlink_failed()
1367 self.failed_test_cases_info.add(self.current_test_case_info)
1368 if is_core_present(self.current_test_case_info.tempdir):
1369 if not self.current_test_case_info.core_crash_test:
1370 if isinstance(test, unittest.suite._ErrorHolder):
1371 test_name = str(test)
1372 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001373 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001374 get_testcase_doc_name(test), test.id())
1375 self.current_test_case_info.core_crash_test = test_name
1376 self.core_crash_test_cases_info.add(
1377 self.current_test_case_info)
1378 else:
1379 self.result_string = '%s [no temp dir]' % error_type_str
1380
1381 self.send_result_through_pipe(test, error_type)
1382
Damjan Marionf56b77a2016-10-03 19:44:57 +02001383 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001384 """
1385 Record a test failed result
1386
1387 :param test:
1388 :param err: error message
1389
1390 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001391 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001392
Damjan Marionf56b77a2016-10-03 19:44:57 +02001393 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001394 """
1395 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001396
Klement Sekeraf62ae122016-10-11 11:47:09 +02001397 :param test:
1398 :param err: error message
1399
1400 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001401 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001402
Damjan Marionf56b77a2016-10-03 19:44:57 +02001403 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001404 """
1405 Get test description
1406
1407 :param test:
1408 :returns: test description
1409
1410 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001411 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001412
Damjan Marionf56b77a2016-10-03 19:44:57 +02001413 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001414 """
1415 Start a test
1416
1417 :param test:
1418
1419 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001420
1421 def print_header(test):
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001422 test_doc = getdoc(test)
1423 if not test_doc:
1424 raise Exception("No doc string for test '%s'" % test.id())
1425 test_title = test_doc.splitlines()[0]
1426 test_title_colored = colorize(test_title, GREEN)
1427 if test.force_solo():
1428 # long live PEP-8 and 80 char width limitation...
1429 c = YELLOW
1430 test_title_colored = colorize("SOLO RUN: " + test_title, c)
1431
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001432 if not hasattr(test.__class__, '_header_printed'):
1433 print(double_line_delim)
Andrew Yourtchenkoa3b7c552020-08-26 14:33:54 +00001434 print(test_title_colored)
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001435 print(double_line_delim)
1436 test.__class__._header_printed = True
1437
1438 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001439 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001440 unittest.TestResult.startTest(self, test)
1441 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001442 self.stream.writeln(
1443 "Starting " + self.getDescription(test) + " ...")
1444 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001445
Damjan Marionf56b77a2016-10-03 19:44:57 +02001446 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001447 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001448 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001449
1450 :param test:
1451
1452 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001453 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001454
Damjan Marionf56b77a2016-10-03 19:44:57 +02001455 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001456 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001457 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001458 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001459 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001460 else:
Ole Troan0c629322019-11-28 14:48:44 +01001461 self.stream.writeln("%-68s %4.2f %s" %
1462 (self.getDescription(test),
1463 time.time() - self.start_test,
1464 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001465
1466 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001467
Damjan Marionf56b77a2016-10-03 19:44:57 +02001468 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001469 """
1470 Print errors from running the test case
1471 """
juraj.linkesabec0122018-11-16 17:28:56 +01001472 if len(self.errors) > 0 or len(self.failures) > 0:
1473 self.stream.writeln()
1474 self.printErrorList('ERROR', self.errors)
1475 self.printErrorList('FAIL', self.failures)
1476
1477 # ^^ that is the last output from unittest before summary
1478 if not self.runner.print_summary:
1479 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1480 self.stream = devnull
1481 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001482
Damjan Marionf56b77a2016-10-03 19:44:57 +02001483 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001484 """
1485 Print error list to the output stream together with error type
1486 and test case description.
1487
1488 :param flavour: error type
1489 :param errors: iterable errors
1490
1491 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001492 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001493 self.stream.writeln(double_line_delim)
1494 self.stream.writeln("%s: %s" %
1495 (flavour, self.getDescription(test)))
1496 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001497 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001498
1499
Damjan Marionf56b77a2016-10-03 19:44:57 +02001500class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001501 """
Klement Sekera104543f2017-02-03 07:29:43 +01001502 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001503 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001504
Klement Sekeraf62ae122016-10-11 11:47:09 +02001505 @property
1506 def resultclass(self):
1507 """Class maintaining the results of the tests"""
1508 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001509
juraj.linkes184870a2018-07-16 14:22:01 +02001510 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001511 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001512 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001513 # ignore stream setting here, use hard-coded stdout to be in sync
1514 # with prints from VppTestCase methods ...
1515 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1516 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001517 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001518 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001519
juraj.linkesabec0122018-11-16 17:28:56 +01001520 self.orig_stream = self.stream
1521 self.resultclass.test_framework_result_pipe = result_pipe
1522
1523 self.print_summary = print_summary
1524
1525 def _makeResult(self):
1526 return self.resultclass(self.stream,
1527 self.descriptions,
1528 self.verbosity,
1529 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001530
Damjan Marionf56b77a2016-10-03 19:44:57 +02001531 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001532 """
1533 Run the tests
1534
1535 :param test:
1536
1537 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001538 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001539
1540 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001541 if not self.print_summary:
1542 self.stream = self.orig_stream
1543 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001544 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001545
1546
1547class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001548 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1549 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001550 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001551 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001552 if hasattr(self, 'testcase') and self.testcase.debug_all:
1553 if self.testcase.debug_gdbserver:
1554 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1555 .format(port=self.testcase.gdbserver_port)] + args
1556 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1557 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001558 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001559 self.app_name = os.path.basename(self.app_bin)
1560 if hasattr(self, 'role'):
1561 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001562 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001563 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001564 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001565 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001566
Dave Wallace24564332019-10-21 02:53:14 +00001567 def wait_for_enter(self):
1568 if not hasattr(self, 'testcase'):
1569 return
1570 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1571 print()
1572 print(double_line_delim)
1573 print("Spawned GDB Server for '{app}' with PID: {pid}"
1574 .format(app=self.app_name, pid=self.process.pid))
1575 elif self.testcase.debug_all and self.testcase.debug_gdb:
1576 print()
1577 print(double_line_delim)
1578 print("Spawned '{app}' with PID: {pid}"
1579 .format(app=self.app_name, pid=self.process.pid))
1580 else:
1581 return
1582 print(single_line_delim)
1583 print("You can debug '{app}' using:".format(app=self.app_name))
1584 if self.testcase.debug_gdbserver:
1585 print("sudo gdb " + self.app_bin +
1586 " -ex 'target remote localhost:{port}'"
1587 .format(port=self.testcase.gdbserver_port))
1588 print("Now is the time to attach gdb by running the above "
1589 "command, set up breakpoints etc., then resume from "
1590 "within gdb by issuing the 'continue' command")
1591 self.testcase.gdbserver_port += 1
1592 elif self.testcase.debug_gdb:
1593 print("sudo gdb " + self.app_bin +
1594 " -ex 'attach {pid}'".format(pid=self.process.pid))
1595 print("Now is the time to attach gdb by running the above "
1596 "command and set up breakpoints etc., then resume from"
1597 " within gdb by issuing the 'continue' command")
1598 print(single_line_delim)
1599 input("Press ENTER to continue running the testcase...")
1600
Neale Ranns812ed392017-10-16 04:20:13 -07001601 def run(self):
1602 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001603 if not os.path.exists(executable) or not os.access(
1604 executable, os.F_OK | os.X_OK):
1605 # Exit code that means some system file did not exist,
1606 # could not be opened, or had some other kind of error.
1607 self.result = os.EX_OSFILE
1608 raise EnvironmentError(
1609 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001610 self.logger.debug("Running executable: '{app}'"
1611 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001612 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001613 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001614 env["CK_LOG_FILE_NAME"] = "-"
1615 self.process = subprocess.Popen(
1616 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1617 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001618 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001619 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001620 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001621 self.logger.info("Return code is `%s'" % self.process.returncode)
1622 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001623 self.logger.info("Executable `{app}' wrote to stdout:"
1624 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001625 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001626 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001627 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001628 self.logger.info("Executable `{app}' wrote to stderr:"
1629 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001630 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001631 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001632 self.logger.info(single_line_delim)
1633 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001634
Klement Sekera6aa58b72019-05-16 14:34:55 +02001635
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001636if __name__ == '__main__':
1637 pass