blob: c9d72a768bb0e714436138d54ddb45918e7070fd [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
302 def instance(cls):
303 """Return the instance of this testcase"""
304 return cls.test_instance
305
Damjan Marionf56b77a2016-10-03 19:44:57 +0200306 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200307 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000308 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200309 cls.debug_core = False
310 cls.debug_gdb = False
311 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000312 cls.debug_all = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200313 if d is None:
314 return
315 dl = d.lower()
316 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200317 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000318 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200319 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000320 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200321 cls.debug_gdbserver = True
322 else:
323 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000324 if dl == "gdb-all" or dl == "gdbserver-all":
325 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200326
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800327 @staticmethod
328 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200329 cpu_usage_list = [set(range(psutil.cpu_count()))]
330 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
331 if 'vpp_main' == p.info['name']]
332 for vpp_process in vpp_processes:
333 for cpu_usage_set in cpu_usage_list:
334 try:
335 cpu_num = vpp_process.cpu_num()
336 if cpu_num in cpu_usage_set:
337 cpu_usage_set_index = cpu_usage_list.index(
338 cpu_usage_set)
339 if cpu_usage_set_index == len(cpu_usage_list) - 1:
340 cpu_usage_list.append({cpu_num})
341 else:
342 cpu_usage_list[cpu_usage_set_index + 1].add(
343 cpu_num)
344 cpu_usage_set.remove(cpu_num)
345 break
346 except psutil.NoSuchProcess:
347 pass
348
349 for cpu_usage_set in cpu_usage_list:
350 if len(cpu_usage_set) > 0:
351 min_usage_set = cpu_usage_set
352 break
353
354 return random.choice(tuple(min_usage_set))
355
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800356 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200357 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200358 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400359 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100360 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400361 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100362 c = os.getenv("CACHE_OUTPUT", "1")
363 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200364 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100365 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
366 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400367 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100368 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
369 plugin_path = None
370 if cls.plugin_path is not None:
371 if cls.extern_plugin_path is not None:
372 plugin_path = "%s:%s" % (
373 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100374 else:
375 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100376 elif cls.extern_plugin_path is not None:
377 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100378 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100379 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100380 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100381 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100382 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100383 if size is not None:
384 coredump_size = "coredump-size %s" % size
385 if coredump_size is None:
386 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200387
Ole Troana45dc072018-12-21 16:04:22 +0100388 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000389 if not hasattr(cls, "worker_config"):
390 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200391
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000392 default_variant = os.getenv("VARIANT")
393 if default_variant is not None:
394 default_variant = "defaults { %s 100 }" % default_variant
395 else:
396 default_variant = ""
397
Dave Barach77841402020-04-29 17:04:10 -0400398 api_fuzzing = os.getenv("API_FUZZ")
399 if api_fuzzing is None:
400 api_fuzzing = 'off'
401
Ole Troana45dc072018-12-21 16:04:22 +0100402 cls.vpp_cmdline = [cls.vpp_bin, "unix",
403 "{", "nodaemon", debug_cli, "full-coredump",
404 coredump_size, "runtime-dir", cls.tempdir, "}",
405 "api-trace", "{", "on", "}", "api-segment", "{",
406 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000407 "main-core", str(cpu_core_number),
408 cls.worker_config, "}",
Dave Barach4ed25982019-12-25 09:24:58 -0500409 "physmem", "{", "max-size", "32m", "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200410 "statseg", "{", "socket-name", cls.stats_sock, "}",
411 "socksvr", "{", "socket-name", cls.api_sock, "}",
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000412 "node { ", default_variant, "}",
Dave Barach77841402020-04-29 17:04:10 -0400413 "api-fuzz {", api_fuzzing, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200414 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100415 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200416 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100417 "}", "plugin", "unittest_plugin.so", "{", "enable",
418 "}"] + cls.extra_vpp_plugin_config + ["}", ]
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000419
Ole Troana45dc072018-12-21 16:04:22 +0100420 if cls.extra_vpp_punt_config is not None:
421 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100422 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100423 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400424 if cls.test_plugin_path is not None:
425 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
426
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100427 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
428 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200429
430 @classmethod
431 def wait_for_enter(cls):
432 if cls.debug_gdbserver:
433 print(double_line_delim)
434 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
435 elif cls.debug_gdb:
436 print(double_line_delim)
437 print("Spawned VPP with PID: %d" % cls.vpp.pid)
438 else:
439 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
440 return
441 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000442 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200443 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400444 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000445 " -ex 'target remote localhost:{port}'"
446 .format(port=cls.gdbserver_port))
447 print("Now is the time to attach gdb by running the above "
448 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200449 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000450 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200451 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400452 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000453 print("Now is the time to attach gdb by running the above "
454 "command and set up breakpoints etc., then resume VPP from"
455 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200456 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800457 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200458
459 @classmethod
460 def run_vpp(cls):
461 cmdline = cls.vpp_cmdline
462
463 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100464 gdbserver = '/usr/bin/gdbserver'
465 if not os.path.isfile(gdbserver) or \
466 not os.access(gdbserver, os.X_OK):
467 raise Exception("gdbserver binary '%s' does not exist or is "
468 "not executable" % gdbserver)
469
Dave Wallace24564332019-10-21 02:53:14 +0000470 cmdline = [gdbserver, 'localhost:{port}'
471 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200472 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
473
Klement Sekera931be3a2016-11-03 05:36:01 +0100474 try:
475 cls.vpp = subprocess.Popen(cmdline,
476 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100477 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800478 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800479 cls.logger.critical("Subprocess returned with non-0 return code: ("
480 "%s)", e.returncode)
481 raise
482 except OSError as e:
483 cls.logger.critical("Subprocess returned with OS error: "
484 "(%s) %s", e.errno, e.strerror)
485 raise
486 except Exception as e:
487 cls.logger.exception("Subprocess returned unexpected from "
488 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100489 raise
490
Klement Sekera277b89c2016-10-28 13:20:27 +0200491 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100492
Damjan Marionf56b77a2016-10-03 19:44:57 +0200493 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000494 def wait_for_coredump(cls):
495 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400496 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000497 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400498 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000499 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400500 ok = False
501 while time.time() < deadline:
502 cls.sleep(1)
503 size = curr_size
504 curr_size = os.path.getsize(corefile)
505 if size == curr_size:
506 ok = True
507 break
508 if not ok:
509 cls.logger.error("Timed out waiting for coredump to complete:"
510 " %s", corefile)
511 else:
512 cls.logger.error("Coredump complete: %s, size %d",
513 corefile, curr_size)
514
515 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200516 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200517 """
518 Perform class setup before running the testcase
519 Remove shared memory files, start vpp and connect the vpp-api
520 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800521 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100522 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100523 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000524 seed = os.environ["RND_SEED"]
525 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100526 if hasattr(cls, 'parallel_handler'):
527 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100528 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700529
Klement Sekeraf62ae122016-10-11 11:47:09 +0200530 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200531 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200532 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200533 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200534 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
535 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100536 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
537 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200538 cls.file_handler.setLevel(DEBUG)
539 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700540 cls.logger.debug("--- setUpClass() for %s called ---" %
541 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200542 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200543 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200544 cls.logger.info("Temporary dir is %s, shm prefix is %s",
545 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000546 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200547 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100548 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100549 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200550 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100551 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100552 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200553 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200554 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200555 # need to catch exceptions here because if we raise, then the cleanup
556 # doesn't get called and we might end with a zombie vpp
557 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200558 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200559 cls.reporter.send_keep_alive(cls, 'setUpClass')
560 VppTestResult.current_test_case_info = TestCaseInfo(
561 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100562 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100563 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100564 cls.pump_thread_stop_flag = Event()
565 cls.pump_thread_wakeup_pipe = os.pipe()
566 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100567 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100568 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200569 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400570 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200571 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400572 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100573 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400574 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100575 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400576 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100577 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200578 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200579 try:
580 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100581 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200582 cls.vpp_startup_failed = True
583 cls.logger.critical(
584 "VPP died shortly after startup, check the"
585 " output to standard error for possible cause")
586 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100587 try:
588 cls.vapi.connect()
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500589 except vpp_papi.VPPIOError as e:
590 cls.logger.debug("Exception connecting to vapi: %s" % e)
591 cls.vapi.disconnect()
592
Klement Sekera085f5c02016-11-24 01:59:16 +0100593 if cls.debug_gdbserver:
594 print(colorize("You're running VPP inside gdbserver but "
595 "VPP-API connection failed, did you forget "
596 "to 'continue' VPP from within gdb?", RED))
597 raise
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400598 except vpp_papi.VPPRuntimeError as e:
599 cls.logger.debug("%s" % e)
600 cls.quit()
601 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000602 except Exception as e:
603 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400604 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100605 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200606
Damjan Marionf56b77a2016-10-03 19:44:57 +0200607 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500608 def _debug_quit(cls):
609 if (cls.debug_gdbserver or cls.debug_gdb):
610 try:
611 cls.vpp.poll()
612
613 if cls.vpp.returncode is None:
614 print()
615 print(double_line_delim)
616 print("VPP or GDB server is still running")
617 print(single_line_delim)
618 input("When done debugging, press ENTER to kill the "
619 "process and finish running the testcase...")
620 except AttributeError:
621 pass
622
623 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200624 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200625 """
626 Disconnect vpp-api, kill vpp and cleanup shared memory files
627 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500628 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200629
juraj.linkes184870a2018-07-16 14:22:01 +0200630 # first signal that we want to stop the pump thread, then wake it up
631 if hasattr(cls, 'pump_thread_stop_flag'):
632 cls.pump_thread_stop_flag.set()
633 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100634 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100635 if hasattr(cls, 'pump_thread'):
636 cls.logger.debug("Waiting for pump thread to stop")
637 cls.pump_thread.join()
638 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500639 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100640 cls.vpp_stderr_reader_thread.join()
641
Klement Sekeraf62ae122016-10-11 11:47:09 +0200642 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100643 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100644 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700645 cls.logger.debug("Disconnecting class vapi client on %s",
646 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100647 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700648 cls.logger.debug("Deleting class vapi attribute on %s",
649 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100650 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200651 cls.vpp.poll()
652 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000653 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100654 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400655 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100656 cls.logger.debug("Waiting for vpp to die")
657 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700658 cls.logger.debug("Deleting class vpp attribute on %s",
659 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200660 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200661
Klement Sekera3747c752017-04-10 06:30:17 +0200662 if cls.vpp_startup_failed:
663 stdout_log = cls.logger.info
664 stderr_log = cls.logger.critical
665 else:
666 stdout_log = cls.logger.info
667 stderr_log = cls.logger.info
668
Klement Sekerae4504c62016-12-08 10:16:41 +0100669 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200670 stdout_log(single_line_delim)
671 stdout_log('VPP output to stdout while running %s:', cls.__name__)
672 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100673 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200674 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
675 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200676 stdout_log('\n%s', vpp_output)
677 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200678
Klement Sekerae4504c62016-12-08 10:16:41 +0100679 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200680 stderr_log(single_line_delim)
681 stderr_log('VPP output to stderr while running %s:', cls.__name__)
682 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100683 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200684 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
685 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200686 stderr_log('\n%s', vpp_output)
687 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200688
Damjan Marionf56b77a2016-10-03 19:44:57 +0200689 @classmethod
690 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200691 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700692 cls.logger.debug("--- tearDownClass() for %s called ---" %
693 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200694 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200695 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200696 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100697 cls.reset_packet_infos()
698 if debug_framework:
699 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200700
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700701 def show_commands_at_teardown(self):
702 """ Allow subclass specific teardown logging additions."""
703 self.logger.info("--- No test specific show commands provided. ---")
704
Damjan Marionf56b77a2016-10-03 19:44:57 +0200705 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200706 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100707 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
708 (self.__class__.__name__, self._testMethodName,
709 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700710
711 try:
712 if not self.vpp_dead:
713 self.logger.debug(self.vapi.cli("show trace max 1000"))
714 self.logger.info(self.vapi.ppcli("show interface"))
715 self.logger.info(self.vapi.ppcli("show hardware"))
716 self.logger.info(self.statistics.set_errors_str())
717 self.logger.info(self.vapi.ppcli("show run"))
718 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400719 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700720 self.logger.info("Logging testcase specific show commands.")
721 self.show_commands_at_teardown()
722 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500723 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000724 m = self._testMethodName
725 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500726 tmp_api_trace = "/tmp/%s" % api_trace
727 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
728 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
729 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
730 vpp_api_trace_log))
731 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500732 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500733 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700734 except VppTransportShmemIOError:
735 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
736 "Cannot log show commands.")
737 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100738 else:
739 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200740
Damjan Marionf56b77a2016-10-03 19:44:57 +0200741 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200742 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800743 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200744 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100745 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400746
747 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
748 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100749 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100750 self.vpp_stdout_deque.append(
751 "--- test setUp() for %s.%s(%s) starts here ---\n" %
752 (self.__class__.__name__, self._testMethodName,
753 self._testMethodDoc))
754 self.vpp_stderr_deque.append(
755 "--- test setUp() for %s.%s(%s) starts here ---\n" %
756 (self.__class__.__name__, self._testMethodName,
757 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200758 self.vapi.cli("clear trace")
759 # store the test instance inside the test class - so that objects
760 # holding the class can access instance methods (like assertEqual)
761 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200762
Damjan Marionf56b77a2016-10-03 19:44:57 +0200763 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200764 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200765 """
766 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200767
Klement Sekera75e7d132017-09-20 08:26:30 +0200768 :param interfaces: iterable interface indexes (if None,
769 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200770
Klement Sekeraf62ae122016-10-11 11:47:09 +0200771 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200772 if interfaces is None:
773 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200774 for i in interfaces:
775 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200776
Damjan Marionf56b77a2016-10-03 19:44:57 +0200777 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100778 def register_capture(cls, cap_name):
779 """ Register a capture in the testclass """
780 # add to the list of captures with current timestamp
781 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100782
783 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000784 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400785 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
786 # returns float("2.190522")
787 timestr = cls.vapi.cli('show clock')
788 head, sep, tail = timestr.partition(',')
789 head, sep, tail = head.partition('Time now')
790 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000791
792 @classmethod
793 def sleep_on_vpp_time(cls, sec):
794 """ Sleep according to time in VPP world """
795 # On a busy system with many processes
796 # we might end up with VPP time being slower than real world
797 # So take that into account when waiting for VPP to do something
798 start_time = cls.get_vpp_time()
799 while cls.get_vpp_time() - start_time < sec:
800 cls.sleep(0.1)
801
802 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100803 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000804 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100805 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200806 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000807 # PG, when starts, runs to completion -
808 # so let's avoid a race condition,
809 # and wait a little till it's done.
810 # Then clean it up - and then be gone.
811 deadline = time.time() + 300
812 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
813 cls.sleep(0.01) # yield
814 if time.time() > deadline:
815 cls.logger.error("Timeout waiting for pg to stop")
816 break
817 for stamp, cap_name in cls._captures:
818 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100819 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200820
Damjan Marionf56b77a2016-10-03 19:44:57 +0200821 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200822 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200823 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100824 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200825
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100826 :param interfaces: iterable indexes of the interfaces.
827 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200828
Klement Sekeraf62ae122016-10-11 11:47:09 +0200829 """
830 result = []
831 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200832 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200833 setattr(cls, intf.name, intf)
834 result.append(intf)
835 cls.pg_interfaces = result
836 return result
837
Matej Klotton0178d522016-11-04 11:11:44 +0100838 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200839 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100840 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100841 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100842
Klement Sekerab9ef2732018-06-24 22:49:33 +0200843 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100844 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100845 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200846 result = [VppLoInterface(cls) for i in range(count)]
847 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100848 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100849 cls.lo_interfaces = result
850 return result
851
Neale Ranns192b13f2019-03-15 02:16:20 -0700852 @classmethod
853 def create_bvi_interfaces(cls, count):
854 """
855 Create BVI interfaces.
856
857 :param count: number of interfaces created.
858 :returns: List of created interfaces.
859 """
860 result = [VppBviInterface(cls) for i in range(count)]
861 for intf in result:
862 setattr(cls, intf.name, intf)
863 cls.bvi_interfaces = result
864 return result
865
Damjan Marionf56b77a2016-10-03 19:44:57 +0200866 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200867 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200868 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200869 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200870 NOTE: Currently works only when Raw layer is present.
871
872 :param packet: packet
873 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200874 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200875
876 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200877 packet_len = len(packet) + 4
878 extend = size - packet_len
879 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200880 num = (extend // len(padding)) + 1
881 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200882
Klement Sekeradab231a2016-12-21 08:50:14 +0100883 @classmethod
884 def reset_packet_infos(cls):
885 """ Reset the list of packet info objects and packet counts to zero """
886 cls._packet_infos = {}
887 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200888
Klement Sekeradab231a2016-12-21 08:50:14 +0100889 @classmethod
890 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200891 """
892 Create packet info object containing the source and destination indexes
893 and add it to the testcase's packet info list
894
Klement Sekeradab231a2016-12-21 08:50:14 +0100895 :param VppInterface src_if: source interface
896 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200897
898 :returns: _PacketInfo object
899
900 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200901 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100902 info.index = len(cls._packet_infos)
903 info.src = src_if.sw_if_index
904 info.dst = dst_if.sw_if_index
905 if isinstance(dst_if, VppSubInterface):
906 dst_idx = dst_if.parent.sw_if_index
907 else:
908 dst_idx = dst_if.sw_if_index
909 if dst_idx in cls._packet_count_for_dst_if_idx:
910 cls._packet_count_for_dst_if_idx[dst_idx] += 1
911 else:
912 cls._packet_count_for_dst_if_idx[dst_idx] = 1
913 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200914 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200915
Damjan Marionf56b77a2016-10-03 19:44:57 +0200916 @staticmethod
917 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200918 """
919 Convert _PacketInfo object to packet payload
920
921 :param info: _PacketInfo object
922
923 :returns: string containing serialized data from packet info
924 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100925 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
926 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200927
Damjan Marionf56b77a2016-10-03 19:44:57 +0200928 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800929 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200930 """
931 Convert packet payload to _PacketInfo object
932
933 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700934 :type payload: <class 'scapy.packet.Raw'>
935 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800936 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700937 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200938 :returns: _PacketInfo object containing de-serialized data from payload
939
940 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800941 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200942 info = _PacketInfo()
943 info.index = int(numbers[0])
944 info.src = int(numbers[1])
945 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100946 info.ip = int(numbers[3])
947 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200948 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200949
Damjan Marionf56b77a2016-10-03 19:44:57 +0200950 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200951 """
952 Iterate over the packet info list stored in the testcase
953 Start iteration with first element if info is None
954 Continue based on index in info if info is specified
955
956 :param info: info or None
957 :returns: next info in list or None if no more infos
958 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200959 if info is None:
960 next_index = 0
961 else:
962 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100963 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200964 return None
965 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100966 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200967
Klement Sekeraf62ae122016-10-11 11:47:09 +0200968 def get_next_packet_info_for_interface(self, src_index, info):
969 """
970 Search the packet info list for the next packet info with same source
971 interface index
972
973 :param src_index: source interface index to search for
974 :param info: packet info - where to start the search
975 :returns: packet info or None
976
977 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200978 while True:
979 info = self.get_next_packet_info(info)
980 if info is None:
981 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200982 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200983 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200984
Klement Sekeraf62ae122016-10-11 11:47:09 +0200985 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
986 """
987 Search the packet info list for the next packet info with same source
988 and destination interface indexes
989
990 :param src_index: source interface index to search for
991 :param dst_index: destination interface index to search for
992 :param info: packet info - where to start the search
993 :returns: packet info or None
994
995 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200996 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200997 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200998 if info is None:
999 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +02001000 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +02001001 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +02001002
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001003 def assert_equal(self, real_value, expected_value, name_or_class=None):
1004 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +01001005 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001006 return
1007 try:
1008 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1009 msg = msg % (getdoc(name_or_class).strip(),
1010 real_value, str(name_or_class(real_value)),
1011 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001012 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001013 msg = "Invalid %s: %s does not match expected value %s" % (
1014 name_or_class, real_value, expected_value)
1015
1016 self.assertEqual(real_value, expected_value, msg)
1017
Klement Sekerab17dd962017-01-09 07:43:48 +01001018 def assert_in_range(self,
1019 real_value,
1020 expected_min,
1021 expected_max,
1022 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001023 if name is None:
1024 msg = None
1025 else:
1026 msg = "Invalid %s: %s out of range <%s,%s>" % (
1027 name, real_value, expected_min, expected_max)
1028 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1029
Klement Sekerad81ae412018-05-16 10:52:54 +02001030 def assert_packet_checksums_valid(self, packet,
1031 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001032 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001033 udp_layers = ['UDP', 'UDPerror']
1034 checksum_fields = ['cksum', 'chksum']
1035 checksums = []
1036 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001037 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001038 while True:
1039 layer = temp.getlayer(counter)
1040 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001041 layer = layer.copy()
1042 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001043 for cf in checksum_fields:
1044 if hasattr(layer, cf):
1045 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001046 0 == getattr(layer, cf) and \
1047 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001048 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001049 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001050 checksums.append((counter, cf))
1051 else:
1052 break
1053 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001054 if 0 == len(checksums):
1055 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001056 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001057 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001058 calc_sum = getattr(temp[layer], cf)
1059 self.assert_equal(
1060 getattr(received[layer], cf), calc_sum,
1061 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1062 self.logger.debug(
1063 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1064 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001065
1066 def assert_checksum_valid(self, received_packet, layer,
1067 field_name='chksum',
1068 ignore_zero_checksum=False):
1069 """ Check checksum of received packet on given layer """
1070 received_packet_checksum = getattr(received_packet[layer], field_name)
1071 if ignore_zero_checksum and 0 == received_packet_checksum:
1072 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001073 recalculated = received_packet.__class__(
1074 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001075 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001076 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001077 self.assert_equal(received_packet_checksum,
1078 getattr(recalculated[layer], field_name),
1079 "packet checksum on layer: %s" % layer)
1080
1081 def assert_ip_checksum_valid(self, received_packet,
1082 ignore_zero_checksum=False):
1083 self.assert_checksum_valid(received_packet, 'IP',
1084 ignore_zero_checksum=ignore_zero_checksum)
1085
1086 def assert_tcp_checksum_valid(self, received_packet,
1087 ignore_zero_checksum=False):
1088 self.assert_checksum_valid(received_packet, 'TCP',
1089 ignore_zero_checksum=ignore_zero_checksum)
1090
1091 def assert_udp_checksum_valid(self, received_packet,
1092 ignore_zero_checksum=True):
1093 self.assert_checksum_valid(received_packet, 'UDP',
1094 ignore_zero_checksum=ignore_zero_checksum)
1095
1096 def assert_embedded_icmp_checksum_valid(self, received_packet):
1097 if received_packet.haslayer(IPerror):
1098 self.assert_checksum_valid(received_packet, 'IPerror')
1099 if received_packet.haslayer(TCPerror):
1100 self.assert_checksum_valid(received_packet, 'TCPerror')
1101 if received_packet.haslayer(UDPerror):
1102 self.assert_checksum_valid(received_packet, 'UDPerror',
1103 ignore_zero_checksum=True)
1104 if received_packet.haslayer(ICMPerror):
1105 self.assert_checksum_valid(received_packet, 'ICMPerror')
1106
1107 def assert_icmp_checksum_valid(self, received_packet):
1108 self.assert_checksum_valid(received_packet, 'ICMP')
1109 self.assert_embedded_icmp_checksum_valid(received_packet)
1110
1111 def assert_icmpv6_checksum_valid(self, pkt):
1112 if pkt.haslayer(ICMPv6DestUnreach):
1113 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1114 self.assert_embedded_icmp_checksum_valid(pkt)
1115 if pkt.haslayer(ICMPv6EchoRequest):
1116 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1117 if pkt.haslayer(ICMPv6EchoReply):
1118 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1119
Klement Sekera3a343d42019-05-16 14:35:46 +02001120 def get_packet_counter(self, counter):
1121 if counter.startswith("/"):
1122 counter_value = self.statistics.get_counter(counter)
1123 else:
1124 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001125 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001126 for i in range(1, len(counters) - 1):
1127 results = counters[i].split()
1128 if results[1] == counter:
1129 counter_value = int(results[0])
1130 break
1131 return counter_value
1132
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001133 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001134 counter_value = self.get_packet_counter(counter)
1135 self.assert_equal(counter_value, expected_value,
1136 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001137
Ole Troan233e4682019-05-16 15:01:34 +02001138 def assert_error_counter_equal(self, counter, expected_value):
1139 counter_value = self.statistics.get_err_counter(counter)
1140 self.assert_equal(counter_value, expected_value,
1141 "error counter `%s'" % counter)
1142
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001143 @classmethod
1144 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001145
1146 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1147 # * by Guido, only the main thread can be interrupted.
1148 # */
1149 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1150 if timeout == 0:
1151 # yield quantum
1152 if hasattr(os, 'sched_yield'):
1153 os.sched_yield()
1154 else:
1155 time.sleep(0)
1156 return
1157
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001158 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001159 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001160 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001161 after = time.time()
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001162 if after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001163 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001164 "slept for %es instead of ~%es!",
1165 after - before, timeout)
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001166
1167 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001168 "Finished sleep (%s) - slept %es (wanted %es)",
1169 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001170
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001171 def pg_send(self, intf, pkts, worker=None):
Neale Ranns52fae862018-01-08 04:41:42 -08001172 self.vapi.cli("clear trace")
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001173 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001174 self.pg_enable_capture(self.pg_interfaces)
1175 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001176
1177 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1178 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001179 if not timeout:
1180 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001181 for i in self.pg_interfaces:
1182 i.get_capture(0, timeout=timeout)
1183 i.assert_nothing_captured(remark=remark)
1184 timeout = 0.1
1185
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001186 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None):
Neale Rannsd7603d92019-03-28 08:56:10 +00001187 if not n_rx:
1188 n_rx = len(pkts)
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001189 self.pg_send(intf, pkts, worker=worker)
Neale Rannsd7603d92019-03-28 08:56:10 +00001190 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001191 return rx
1192
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001193 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1194 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001195 rx = output.get_capture(len(pkts))
1196 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001197 if not timeout:
1198 timeout = 1
1199 for i in self.pg_interfaces:
1200 if i not in outputs:
1201 i.get_capture(0, timeout=timeout)
1202 i.assert_nothing_captured()
1203 timeout = 0.1
1204
Neale Ranns52fae862018-01-08 04:41:42 -08001205 return rx
1206
Damjan Marionf56b77a2016-10-03 19:44:57 +02001207
juraj.linkes184870a2018-07-16 14:22:01 +02001208def get_testcase_doc_name(test):
1209 return getdoc(test.__class__).splitlines()[0]
1210
1211
Ole Trøan5ba91592018-11-22 10:01:09 +00001212def get_test_description(descriptions, test):
1213 short_description = test.shortDescription()
1214 if descriptions and short_description:
1215 return short_description
1216 else:
1217 return str(test)
1218
1219
juraj.linkes40dd73b2018-09-21 13:55:16 +02001220class TestCaseInfo(object):
1221 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1222 self.logger = logger
1223 self.tempdir = tempdir
1224 self.vpp_pid = vpp_pid
1225 self.vpp_bin_path = vpp_bin_path
1226 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001227
1228
Damjan Marionf56b77a2016-10-03 19:44:57 +02001229class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001230 """
1231 @property result_string
1232 String variable to store the test case result string.
1233 @property errors
1234 List variable containing 2-tuples of TestCase instances and strings
1235 holding formatted tracebacks. Each tuple represents a test which
1236 raised an unexpected exception.
1237 @property failures
1238 List variable containing 2-tuples of TestCase instances and strings
1239 holding formatted tracebacks. Each tuple represents a test where
1240 a failure was explicitly signalled using the TestCase.assert*()
1241 methods.
1242 """
1243
juraj.linkes40dd73b2018-09-21 13:55:16 +02001244 failed_test_cases_info = set()
1245 core_crash_test_cases_info = set()
1246 current_test_case_info = None
1247
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001248 def __init__(self, stream=None, descriptions=None, verbosity=None,
1249 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001250 """
Klement Sekerada505f62017-01-04 12:58:53 +01001251 :param stream File descriptor to store where to report test results.
1252 Set to the standard error stream by default.
1253 :param descriptions Boolean variable to store information if to use
1254 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001255 :param verbosity Integer variable to store required verbosity level.
1256 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001257 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001258 self.stream = stream
1259 self.descriptions = descriptions
1260 self.verbosity = verbosity
1261 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001262 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001263
Damjan Marionf56b77a2016-10-03 19:44:57 +02001264 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001265 """
1266 Record a test succeeded result
1267
1268 :param test:
1269
1270 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001271 if self.current_test_case_info:
1272 self.current_test_case_info.logger.debug(
1273 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1274 test._testMethodName,
1275 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001276 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001277 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001278
juraj.linkescae64f82018-09-19 15:01:47 +02001279 self.send_result_through_pipe(test, PASS)
1280
Klement Sekeraf62ae122016-10-11 11:47:09 +02001281 def addSkip(self, test, reason):
1282 """
1283 Record a test skipped.
1284
1285 :param test:
1286 :param reason:
1287
1288 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001289 if self.current_test_case_info:
1290 self.current_test_case_info.logger.debug(
1291 "--- addSkip() %s.%s(%s) called, reason is %s" %
1292 (test.__class__.__name__, test._testMethodName,
1293 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001294 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001295 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001296
juraj.linkescae64f82018-09-19 15:01:47 +02001297 self.send_result_through_pipe(test, SKIP)
1298
juraj.linkes40dd73b2018-09-21 13:55:16 +02001299 def symlink_failed(self):
1300 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001301 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001302 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001303 link_path = os.path.join(
1304 failed_dir,
1305 '%s-FAILED' %
1306 os.path.basename(self.current_test_case_info.tempdir))
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001307
1308 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001309 "creating a link to the failed test")
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001310 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001311 "os.symlink(%s, %s)" %
1312 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001313 if os.path.exists(link_path):
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001314 self.current_test_case_info.logger.debug(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001315 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001316 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001317 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001318
Klement Sekeraf413bef2017-08-15 07:09:02 +02001319 except Exception as e:
Paul Vinciguerra9beabd82019-12-01 22:24:28 -05001320 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001321
juraj.linkescae64f82018-09-19 15:01:47 +02001322 def send_result_through_pipe(self, test, result):
1323 if hasattr(self, 'test_framework_result_pipe'):
1324 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001325 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001326 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001327
juraj.linkes40dd73b2018-09-21 13:55:16 +02001328 def log_error(self, test, err, fn_name):
1329 if self.current_test_case_info:
1330 if isinstance(test, unittest.suite._ErrorHolder):
1331 test_name = test.description
1332 else:
1333 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1334 test._testMethodName,
1335 test._testMethodDoc)
1336 self.current_test_case_info.logger.debug(
1337 "--- %s() %s called, err is %s" %
1338 (fn_name, test_name, err))
1339 self.current_test_case_info.logger.debug(
1340 "formatted exception is:\n%s" %
1341 "".join(format_exception(*err)))
1342
1343 def add_error(self, test, err, unittest_fn, error_type):
1344 if error_type == FAIL:
1345 self.log_error(test, err, 'addFailure')
1346 error_type_str = colorize("FAIL", RED)
1347 elif error_type == ERROR:
1348 self.log_error(test, err, 'addError')
1349 error_type_str = colorize("ERROR", RED)
1350 else:
1351 raise Exception('Error type %s cannot be used to record an '
1352 'error or a failure' % error_type)
1353
1354 unittest_fn(self, test, err)
1355 if self.current_test_case_info:
1356 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1357 (error_type_str,
1358 self.current_test_case_info.tempdir)
1359 self.symlink_failed()
1360 self.failed_test_cases_info.add(self.current_test_case_info)
1361 if is_core_present(self.current_test_case_info.tempdir):
1362 if not self.current_test_case_info.core_crash_test:
1363 if isinstance(test, unittest.suite._ErrorHolder):
1364 test_name = str(test)
1365 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001366 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001367 get_testcase_doc_name(test), test.id())
1368 self.current_test_case_info.core_crash_test = test_name
1369 self.core_crash_test_cases_info.add(
1370 self.current_test_case_info)
1371 else:
1372 self.result_string = '%s [no temp dir]' % error_type_str
1373
1374 self.send_result_through_pipe(test, error_type)
1375
Damjan Marionf56b77a2016-10-03 19:44:57 +02001376 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001377 """
1378 Record a test failed result
1379
1380 :param test:
1381 :param err: error message
1382
1383 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001384 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001385
Damjan Marionf56b77a2016-10-03 19:44:57 +02001386 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001387 """
1388 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001389
Klement Sekeraf62ae122016-10-11 11:47:09 +02001390 :param test:
1391 :param err: error message
1392
1393 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001394 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001395
Damjan Marionf56b77a2016-10-03 19:44:57 +02001396 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001397 """
1398 Get test description
1399
1400 :param test:
1401 :returns: test description
1402
1403 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001404 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001405
Damjan Marionf56b77a2016-10-03 19:44:57 +02001406 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001407 """
1408 Start a test
1409
1410 :param test:
1411
1412 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001413
1414 def print_header(test):
1415 if not hasattr(test.__class__, '_header_printed'):
1416 print(double_line_delim)
1417 print(colorize(getdoc(test).splitlines()[0], GREEN))
1418 print(double_line_delim)
1419 test.__class__._header_printed = True
1420
1421 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001422 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001423 unittest.TestResult.startTest(self, test)
1424 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001425 self.stream.writeln(
1426 "Starting " + self.getDescription(test) + " ...")
1427 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001428
Damjan Marionf56b77a2016-10-03 19:44:57 +02001429 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001430 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001431 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001432
1433 :param test:
1434
1435 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001436 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001437
Damjan Marionf56b77a2016-10-03 19:44:57 +02001438 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001439 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001440 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001441 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001442 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001443 else:
Ole Troan0c629322019-11-28 14:48:44 +01001444 self.stream.writeln("%-68s %4.2f %s" %
1445 (self.getDescription(test),
1446 time.time() - self.start_test,
1447 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001448
1449 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001450
Damjan Marionf56b77a2016-10-03 19:44:57 +02001451 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001452 """
1453 Print errors from running the test case
1454 """
juraj.linkesabec0122018-11-16 17:28:56 +01001455 if len(self.errors) > 0 or len(self.failures) > 0:
1456 self.stream.writeln()
1457 self.printErrorList('ERROR', self.errors)
1458 self.printErrorList('FAIL', self.failures)
1459
1460 # ^^ that is the last output from unittest before summary
1461 if not self.runner.print_summary:
1462 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1463 self.stream = devnull
1464 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001465
Damjan Marionf56b77a2016-10-03 19:44:57 +02001466 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001467 """
1468 Print error list to the output stream together with error type
1469 and test case description.
1470
1471 :param flavour: error type
1472 :param errors: iterable errors
1473
1474 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001475 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001476 self.stream.writeln(double_line_delim)
1477 self.stream.writeln("%s: %s" %
1478 (flavour, self.getDescription(test)))
1479 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001480 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001481
1482
Damjan Marionf56b77a2016-10-03 19:44:57 +02001483class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001484 """
Klement Sekera104543f2017-02-03 07:29:43 +01001485 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001486 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001487
Klement Sekeraf62ae122016-10-11 11:47:09 +02001488 @property
1489 def resultclass(self):
1490 """Class maintaining the results of the tests"""
1491 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001492
juraj.linkes184870a2018-07-16 14:22:01 +02001493 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001494 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001495 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001496 # ignore stream setting here, use hard-coded stdout to be in sync
1497 # with prints from VppTestCase methods ...
1498 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1499 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001500 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001501 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001502
juraj.linkesabec0122018-11-16 17:28:56 +01001503 self.orig_stream = self.stream
1504 self.resultclass.test_framework_result_pipe = result_pipe
1505
1506 self.print_summary = print_summary
1507
1508 def _makeResult(self):
1509 return self.resultclass(self.stream,
1510 self.descriptions,
1511 self.verbosity,
1512 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001513
Damjan Marionf56b77a2016-10-03 19:44:57 +02001514 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001515 """
1516 Run the tests
1517
1518 :param test:
1519
1520 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001521 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001522
1523 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001524 if not self.print_summary:
1525 self.stream = self.orig_stream
1526 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001527 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001528
1529
1530class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001531 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1532 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001533 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001534 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001535 if hasattr(self, 'testcase') and self.testcase.debug_all:
1536 if self.testcase.debug_gdbserver:
1537 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1538 .format(port=self.testcase.gdbserver_port)] + args
1539 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1540 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001541 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001542 self.app_name = os.path.basename(self.app_bin)
1543 if hasattr(self, 'role'):
1544 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001545 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001546 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001547 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001548 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001549
Dave Wallace24564332019-10-21 02:53:14 +00001550 def wait_for_enter(self):
1551 if not hasattr(self, 'testcase'):
1552 return
1553 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1554 print()
1555 print(double_line_delim)
1556 print("Spawned GDB Server for '{app}' with PID: {pid}"
1557 .format(app=self.app_name, pid=self.process.pid))
1558 elif self.testcase.debug_all and self.testcase.debug_gdb:
1559 print()
1560 print(double_line_delim)
1561 print("Spawned '{app}' with PID: {pid}"
1562 .format(app=self.app_name, pid=self.process.pid))
1563 else:
1564 return
1565 print(single_line_delim)
1566 print("You can debug '{app}' using:".format(app=self.app_name))
1567 if self.testcase.debug_gdbserver:
1568 print("sudo gdb " + self.app_bin +
1569 " -ex 'target remote localhost:{port}'"
1570 .format(port=self.testcase.gdbserver_port))
1571 print("Now is the time to attach gdb by running the above "
1572 "command, set up breakpoints etc., then resume from "
1573 "within gdb by issuing the 'continue' command")
1574 self.testcase.gdbserver_port += 1
1575 elif self.testcase.debug_gdb:
1576 print("sudo gdb " + self.app_bin +
1577 " -ex 'attach {pid}'".format(pid=self.process.pid))
1578 print("Now is the time to attach gdb by running the above "
1579 "command and set up breakpoints etc., then resume from"
1580 " within gdb by issuing the 'continue' command")
1581 print(single_line_delim)
1582 input("Press ENTER to continue running the testcase...")
1583
Neale Ranns812ed392017-10-16 04:20:13 -07001584 def run(self):
1585 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001586 if not os.path.exists(executable) or not os.access(
1587 executable, os.F_OK | os.X_OK):
1588 # Exit code that means some system file did not exist,
1589 # could not be opened, or had some other kind of error.
1590 self.result = os.EX_OSFILE
1591 raise EnvironmentError(
1592 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001593 self.logger.debug("Running executable: '{app}'"
1594 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001595 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001596 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001597 env["CK_LOG_FILE_NAME"] = "-"
1598 self.process = subprocess.Popen(
1599 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1600 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001601 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001602 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001603 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001604 self.logger.info("Return code is `%s'" % self.process.returncode)
1605 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001606 self.logger.info("Executable `{app}' wrote to stdout:"
1607 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001608 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001609 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001610 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001611 self.logger.info("Executable `{app}' wrote to stderr:"
1612 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001613 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001614 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001615 self.logger.info(single_line_delim)
1616 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001617
Klement Sekera6aa58b72019-05-16 14:34:55 +02001618
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001619if __name__ == '__main__':
1620 pass