blob: 9eea8bb03a50383bbcb15313897fe84796fbf219 [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 Vinciguerra72f00042018-11-25 11:05:13 -08005import sys
Ole Trøan162989e2018-11-26 10:27:50 +00006import os
7import select
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04008import signal
Ole Trøan162989e2018-11-26 10:27:50 +00009import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +020010import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020011import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080012import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000013import random
14import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080015import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010016import platform
Ole Trøan162989e2018-11-26 10:27:50 +000017from collections import deque
18from threading import Thread, Event
19from inspect import getdoc, isclass
20from traceback import format_exception
21from logging import FileHandler, DEBUG, Formatter
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070022
23import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000024from scapy.packet import Raw
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040025import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080026from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010027from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000028from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070029from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000030from vpp_papi_provider import VppPapiProvider
Paul Vinciguerra1043fd32019-12-02 21:42:28 -050031import vpp_papi
Ole Trøan162989e2018-11-26 10:27:50 +000032from vpp_papi.vpp_stats import VPPStats
Paul Vinciguerra499ea642019-03-15 09:39:19 -070033from vpp_papi.vpp_transport_shmem import VppTransportShmemIOError
Ole Trøan162989e2018-11-26 10:27:50 +000034from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
35 get_logger, colorize
36from vpp_object import VppObjectRegistry
37from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020038from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
39from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
40from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080041
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010042if os.name == 'posix' and sys.version_info[0] < 3:
43 # using subprocess32 is recommended by python official documentation
44 # @ https://docs.python.org/2/library/subprocess.html
45 import subprocess32 as subprocess
46else:
47 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020048
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080049# Python2/3 compatible
50try:
51 input = raw_input
52except NameError:
53 pass
54
juraj.linkescae64f82018-09-19 15:01:47 +020055PASS = 0
56FAIL = 1
57ERROR = 2
58SKIP = 3
59TEST_RUN = 4
60
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040061
62class BoolEnvironmentVariable(object):
63
64 def __init__(self, env_var_name, default='n', true_values=None):
65 self.name = env_var_name
66 self.default = default
67 self.true_values = true_values if true_values is not None else \
68 ("y", "yes", "1")
69
70 def __bool__(self):
71 return os.getenv(self.name, self.default).lower() in self.true_values
72
73 if sys.version_info[0] == 2:
74 __nonzero__ = __bool__
75
76 def __repr__(self):
77 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
78 (self.name, self.default, self.true_values)
79
80
81debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
82if debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010083 import debug_internal
84
Klement Sekeraf62ae122016-10-11 11:47:09 +020085"""
86 Test framework module.
87
88 The module provides a set of tools for constructing and running tests and
89 representing the results.
90"""
91
Klement Sekeraf62ae122016-10-11 11:47:09 +020092
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040093class VppDiedError(Exception):
94 """ exception for reporting that the subprocess has died."""
95
96 signals_by_value = {v: k for k, v in signal.__dict__.items() if
97 k.startswith('SIG') and not k.startswith('SIG_')}
98
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040099 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400100 self.rv = rv
101 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400102 self.testcase = testcase
103 self.method_name = method_name
104
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400105 try:
106 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400107 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400108 pass
109
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400110 if testcase is None and method_name is None:
111 in_msg = ''
112 else:
113 in_msg = 'running %s.%s ' % (testcase, method_name)
114
115 msg = "VPP subprocess died %sunexpectedly with return code: %d%s." % (
116 in_msg,
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400117 self.rv,
Paul Vinciguerraf7457522019-07-13 09:35:38 -0400118 ' [%s]' % (self.signal_name if
119 self.signal_name is not None else ''))
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400120 super(VppDiedError, self).__init__(msg)
121
122
Damjan Marionf56b77a2016-10-03 19:44:57 +0200123class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200124 """Private class to create packet info object.
125
126 Help process information about the next packet.
127 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200128 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100129 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200130 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100131 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200132 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100133 #: Store the index of the destination packet generator interface
134 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200135 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100136 #: Store expected ip version
137 ip = -1
138 #: Store expected upper protocol
139 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100140 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200141 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200142
Matej Klotton16a14cd2016-12-07 15:09:13 +0100143 def __eq__(self, other):
144 index = self.index == other.index
145 src = self.src == other.src
146 dst = self.dst == other.dst
147 data = self.data == other.data
148 return index and src and dst and data
149
Klement Sekeraf62ae122016-10-11 11:47:09 +0200150
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100151def pump_output(testclass):
152 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100153 stdout_fragment = ""
154 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400155 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100156 readable = select.select([testclass.vpp.stdout.fileno(),
157 testclass.vpp.stderr.fileno(),
158 testclass.pump_thread_wakeup_pipe[0]],
159 [], [])[0]
160 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100161 read = os.read(testclass.vpp.stdout.fileno(), 102400)
162 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200163 split = read.decode('ascii',
164 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100165 if len(stdout_fragment) > 0:
166 split[0] = "%s%s" % (stdout_fragment, split[0])
167 if len(split) > 0 and split[-1].endswith("\n"):
168 limit = None
169 else:
170 limit = -1
171 stdout_fragment = split[-1]
172 testclass.vpp_stdout_deque.extend(split[:limit])
173 if not testclass.cache_vpp_output:
174 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100175 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100176 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100177 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100178 read = os.read(testclass.vpp.stderr.fileno(), 102400)
179 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200180 split = read.decode('ascii',
181 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100182 if len(stderr_fragment) > 0:
183 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200184 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100185 limit = None
186 else:
187 limit = -1
188 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200189
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100190 testclass.vpp_stderr_deque.extend(split[:limit])
191 if not testclass.cache_vpp_output:
192 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100193 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100194 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800195 # ignoring the dummy pipe here intentionally - the
196 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200197
198
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800199def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400200 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100201
Klement Sekera6aa58b72019-05-16 14:34:55 +0200202
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800203is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100204
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800205
206def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100207 return platform.machine() == 'aarch64'
208
Klement Sekera6aa58b72019-05-16 14:34:55 +0200209
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800210is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100211
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800212
213def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400214 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100215
Klement Sekera6aa58b72019-05-16 14:34:55 +0200216
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800217running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100218
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800219
Dave Barachd498c9e2020-03-10 16:59:39 -0400220def _running_gcov_tests():
221 return BoolEnvironmentVariable("GCOV_TESTS")
222
223
224running_gcov_tests = _running_gcov_tests()
225
226
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800227def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100228 os_id = os.getenv("OS_ID", "")
229 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200230
Klement Sekera6aa58b72019-05-16 14:34:55 +0200231
Klement Sekera3a350702019-09-02 14:26:26 +0000232running_on_centos = _running_on_centos()
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800233
Klement Sekerad3e671e2017-09-29 12:36:37 +0200234
Klement Sekera909a6a12017-08-08 04:33:53 +0200235class KeepAliveReporter(object):
236 """
237 Singleton object which reports test start to parent process
238 """
239 _shared_state = {}
240
241 def __init__(self):
242 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800243 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200244
245 @property
246 def pipe(self):
247 return self._pipe
248
249 @pipe.setter
250 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800251 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200252 raise Exception("Internal error - pipe should only be set once.")
253 self._pipe = pipe
254
juraj.linkes40dd73b2018-09-21 13:55:16 +0200255 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200256 """
257 Write current test tmpdir & desc to keep-alive pipe to signal liveness
258 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200259 if self.pipe is None:
260 # if not running forked..
261 return
262
Klement Sekera909a6a12017-08-08 04:33:53 +0200263 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200264 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200265 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200266 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200267
Dave Wallacee2efd122017-09-30 22:04:21 -0400268 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200269
270
Damjan Marionf56b77a2016-10-03 19:44:57 +0200271class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100272 """This subclass is a base class for VPP test cases that are implemented as
273 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200274 """
275
Ole Troana45dc072018-12-21 16:04:22 +0100276 extra_vpp_punt_config = []
277 extra_vpp_plugin_config = []
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400278 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100279
Klement Sekeraf62ae122016-10-11 11:47:09 +0200280 @property
281 def packet_infos(self):
282 """List of packet infos"""
283 return self._packet_infos
284
Klement Sekeradab231a2016-12-21 08:50:14 +0100285 @classmethod
286 def get_packet_count_for_if_idx(cls, dst_if_index):
287 """Get the number of packet info for specified destination if index"""
288 if dst_if_index in cls._packet_count_for_dst_if_idx:
289 return cls._packet_count_for_dst_if_idx[dst_if_index]
290 else:
291 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200292
293 @classmethod
294 def instance(cls):
295 """Return the instance of this testcase"""
296 return cls.test_instance
297
Damjan Marionf56b77a2016-10-03 19:44:57 +0200298 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200299 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000300 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200301 cls.debug_core = False
302 cls.debug_gdb = False
303 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000304 cls.debug_all = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200305 if d is None:
306 return
307 dl = d.lower()
308 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200309 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000310 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200311 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000312 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200313 cls.debug_gdbserver = True
314 else:
315 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000316 if dl == "gdb-all" or dl == "gdbserver-all":
317 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200318
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800319 @staticmethod
320 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200321 cpu_usage_list = [set(range(psutil.cpu_count()))]
322 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
323 if 'vpp_main' == p.info['name']]
324 for vpp_process in vpp_processes:
325 for cpu_usage_set in cpu_usage_list:
326 try:
327 cpu_num = vpp_process.cpu_num()
328 if cpu_num in cpu_usage_set:
329 cpu_usage_set_index = cpu_usage_list.index(
330 cpu_usage_set)
331 if cpu_usage_set_index == len(cpu_usage_list) - 1:
332 cpu_usage_list.append({cpu_num})
333 else:
334 cpu_usage_list[cpu_usage_set_index + 1].add(
335 cpu_num)
336 cpu_usage_set.remove(cpu_num)
337 break
338 except psutil.NoSuchProcess:
339 pass
340
341 for cpu_usage_set in cpu_usage_list:
342 if len(cpu_usage_set) > 0:
343 min_usage_set = cpu_usage_set
344 break
345
346 return random.choice(tuple(min_usage_set))
347
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800348 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200349 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200350 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400351 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100352 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400353 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100354 c = os.getenv("CACHE_OUTPUT", "1")
355 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200356 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100357 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
358 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400359 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100360 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
361 plugin_path = None
362 if cls.plugin_path is not None:
363 if cls.extern_plugin_path is not None:
364 plugin_path = "%s:%s" % (
365 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100366 else:
367 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100368 elif cls.extern_plugin_path is not None:
369 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100370 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100371 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100372 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100373 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100374 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100375 if size is not None:
376 coredump_size = "coredump-size %s" % size
377 if coredump_size is None:
378 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200379
Ole Troana45dc072018-12-21 16:04:22 +0100380 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000381 if not hasattr(cls, "worker_config"):
382 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200383
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000384 default_variant = os.getenv("VARIANT")
385 if default_variant is not None:
386 default_variant = "defaults { %s 100 }" % default_variant
387 else:
388 default_variant = ""
389
Dave Barach77841402020-04-29 17:04:10 -0400390 api_fuzzing = os.getenv("API_FUZZ")
391 if api_fuzzing is None:
392 api_fuzzing = 'off'
393
Ole Troana45dc072018-12-21 16:04:22 +0100394 cls.vpp_cmdline = [cls.vpp_bin, "unix",
395 "{", "nodaemon", debug_cli, "full-coredump",
396 coredump_size, "runtime-dir", cls.tempdir, "}",
397 "api-trace", "{", "on", "}", "api-segment", "{",
398 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000399 "main-core", str(cpu_core_number),
400 cls.worker_config, "}",
Dave Barach4ed25982019-12-25 09:24:58 -0500401 "physmem", "{", "max-size", "32m", "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200402 "statseg", "{", "socket-name", cls.stats_sock, "}",
403 "socksvr", "{", "socket-name", cls.api_sock, "}",
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000404 "node { ", default_variant, "}",
Dave Barach77841402020-04-29 17:04:10 -0400405 "api-fuzz {", api_fuzzing, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200406 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100407 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200408 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100409 "}", "plugin", "unittest_plugin.so", "{", "enable",
410 "}"] + cls.extra_vpp_plugin_config + ["}", ]
Ray Kinsella4830e4f2020-03-10 14:35:32 +0000411
Ole Troana45dc072018-12-21 16:04:22 +0100412 if cls.extra_vpp_punt_config is not None:
413 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100414 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100415 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400416 if cls.test_plugin_path is not None:
417 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
418
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100419 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
420 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200421
422 @classmethod
423 def wait_for_enter(cls):
424 if cls.debug_gdbserver:
425 print(double_line_delim)
426 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
427 elif cls.debug_gdb:
428 print(double_line_delim)
429 print("Spawned VPP with PID: %d" % cls.vpp.pid)
430 else:
431 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
432 return
433 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000434 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200435 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400436 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000437 " -ex 'target remote localhost:{port}'"
438 .format(port=cls.gdbserver_port))
439 print("Now is the time to attach gdb by running the above "
440 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200441 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000442 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200443 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400444 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000445 print("Now is the time to attach gdb by running the above "
446 "command and set up breakpoints etc., then resume VPP from"
447 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200448 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800449 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200450
451 @classmethod
452 def run_vpp(cls):
453 cmdline = cls.vpp_cmdline
454
455 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100456 gdbserver = '/usr/bin/gdbserver'
457 if not os.path.isfile(gdbserver) or \
458 not os.access(gdbserver, os.X_OK):
459 raise Exception("gdbserver binary '%s' does not exist or is "
460 "not executable" % gdbserver)
461
Dave Wallace24564332019-10-21 02:53:14 +0000462 cmdline = [gdbserver, 'localhost:{port}'
463 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200464 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
465
Klement Sekera931be3a2016-11-03 05:36:01 +0100466 try:
467 cls.vpp = subprocess.Popen(cmdline,
468 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100469 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800470 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800471 cls.logger.critical("Subprocess returned with non-0 return code: ("
472 "%s)", e.returncode)
473 raise
474 except OSError as e:
475 cls.logger.critical("Subprocess returned with OS error: "
476 "(%s) %s", e.errno, e.strerror)
477 raise
478 except Exception as e:
479 cls.logger.exception("Subprocess returned unexpected from "
480 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100481 raise
482
Klement Sekera277b89c2016-10-28 13:20:27 +0200483 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100484
Damjan Marionf56b77a2016-10-03 19:44:57 +0200485 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000486 def wait_for_coredump(cls):
487 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400488 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000489 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400490 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000491 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400492 ok = False
493 while time.time() < deadline:
494 cls.sleep(1)
495 size = curr_size
496 curr_size = os.path.getsize(corefile)
497 if size == curr_size:
498 ok = True
499 break
500 if not ok:
501 cls.logger.error("Timed out waiting for coredump to complete:"
502 " %s", corefile)
503 else:
504 cls.logger.error("Coredump complete: %s, size %d",
505 corefile, curr_size)
506
507 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200508 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200509 """
510 Perform class setup before running the testcase
511 Remove shared memory files, start vpp and connect the vpp-api
512 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800513 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100514 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100515 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000516 seed = os.environ["RND_SEED"]
517 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100518 if hasattr(cls, 'parallel_handler'):
519 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100520 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700521
Klement Sekeraf62ae122016-10-11 11:47:09 +0200522 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200523 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200524 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200525 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200526 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
527 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100528 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
529 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200530 cls.file_handler.setLevel(DEBUG)
531 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700532 cls.logger.debug("--- setUpClass() for %s called ---" %
533 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200534 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200535 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200536 cls.logger.info("Temporary dir is %s, shm prefix is %s",
537 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000538 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200539 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100540 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100541 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200542 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100543 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100544 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200545 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200546 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200547 # need to catch exceptions here because if we raise, then the cleanup
548 # doesn't get called and we might end with a zombie vpp
549 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200550 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200551 cls.reporter.send_keep_alive(cls, 'setUpClass')
552 VppTestResult.current_test_case_info = TestCaseInfo(
553 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100554 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100555 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100556 cls.pump_thread_stop_flag = Event()
557 cls.pump_thread_wakeup_pipe = os.pipe()
558 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100559 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100560 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200561 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400562 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200563 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400564 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100565 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400566 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100567 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400568 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100569 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200570 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200571 try:
572 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100573 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200574 cls.vpp_startup_failed = True
575 cls.logger.critical(
576 "VPP died shortly after startup, check the"
577 " output to standard error for possible cause")
578 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100579 try:
580 cls.vapi.connect()
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500581 except vpp_papi.VPPIOError as e:
582 cls.logger.debug("Exception connecting to vapi: %s" % e)
583 cls.vapi.disconnect()
584
Klement Sekera085f5c02016-11-24 01:59:16 +0100585 if cls.debug_gdbserver:
586 print(colorize("You're running VPP inside gdbserver but "
587 "VPP-API connection failed, did you forget "
588 "to 'continue' VPP from within gdb?", RED))
589 raise
Paul Vinciguerrae64e5ff2020-04-28 00:27:38 -0400590 except vpp_papi.VPPRuntimeError as e:
591 cls.logger.debug("%s" % e)
592 cls.quit()
593 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000594 except Exception as e:
595 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400596 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100597 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200598
Damjan Marionf56b77a2016-10-03 19:44:57 +0200599 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500600 def _debug_quit(cls):
601 if (cls.debug_gdbserver or cls.debug_gdb):
602 try:
603 cls.vpp.poll()
604
605 if cls.vpp.returncode is None:
606 print()
607 print(double_line_delim)
608 print("VPP or GDB server is still running")
609 print(single_line_delim)
610 input("When done debugging, press ENTER to kill the "
611 "process and finish running the testcase...")
612 except AttributeError:
613 pass
614
615 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200616 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200617 """
618 Disconnect vpp-api, kill vpp and cleanup shared memory files
619 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500620 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200621
juraj.linkes184870a2018-07-16 14:22:01 +0200622 # first signal that we want to stop the pump thread, then wake it up
623 if hasattr(cls, 'pump_thread_stop_flag'):
624 cls.pump_thread_stop_flag.set()
625 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100626 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100627 if hasattr(cls, 'pump_thread'):
628 cls.logger.debug("Waiting for pump thread to stop")
629 cls.pump_thread.join()
630 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500631 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100632 cls.vpp_stderr_reader_thread.join()
633
Klement Sekeraf62ae122016-10-11 11:47:09 +0200634 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100635 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100636 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700637 cls.logger.debug("Disconnecting class vapi client on %s",
638 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100639 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700640 cls.logger.debug("Deleting class vapi attribute on %s",
641 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100642 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200643 cls.vpp.poll()
644 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000645 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100646 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400647 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100648 cls.logger.debug("Waiting for vpp to die")
649 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700650 cls.logger.debug("Deleting class vpp attribute on %s",
651 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200652 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200653
Klement Sekera3747c752017-04-10 06:30:17 +0200654 if cls.vpp_startup_failed:
655 stdout_log = cls.logger.info
656 stderr_log = cls.logger.critical
657 else:
658 stdout_log = cls.logger.info
659 stderr_log = cls.logger.info
660
Klement Sekerae4504c62016-12-08 10:16:41 +0100661 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200662 stdout_log(single_line_delim)
663 stdout_log('VPP output to stdout while running %s:', cls.__name__)
664 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100665 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200666 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
667 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200668 stdout_log('\n%s', vpp_output)
669 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200670
Klement Sekerae4504c62016-12-08 10:16:41 +0100671 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200672 stderr_log(single_line_delim)
673 stderr_log('VPP output to stderr while running %s:', cls.__name__)
674 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100675 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200676 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
677 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200678 stderr_log('\n%s', vpp_output)
679 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200680
Damjan Marionf56b77a2016-10-03 19:44:57 +0200681 @classmethod
682 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200683 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700684 cls.logger.debug("--- tearDownClass() for %s called ---" %
685 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200686 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200687 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200688 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100689 cls.reset_packet_infos()
690 if debug_framework:
691 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200692
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700693 def show_commands_at_teardown(self):
694 """ Allow subclass specific teardown logging additions."""
695 self.logger.info("--- No test specific show commands provided. ---")
696
Damjan Marionf56b77a2016-10-03 19:44:57 +0200697 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200698 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100699 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
700 (self.__class__.__name__, self._testMethodName,
701 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700702
703 try:
704 if not self.vpp_dead:
705 self.logger.debug(self.vapi.cli("show trace max 1000"))
706 self.logger.info(self.vapi.ppcli("show interface"))
707 self.logger.info(self.vapi.ppcli("show hardware"))
708 self.logger.info(self.statistics.set_errors_str())
709 self.logger.info(self.vapi.ppcli("show run"))
710 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400711 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700712 self.logger.info("Logging testcase specific show commands.")
713 self.show_commands_at_teardown()
714 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500715 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000716 m = self._testMethodName
717 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500718 tmp_api_trace = "/tmp/%s" % api_trace
719 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
720 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
721 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
722 vpp_api_trace_log))
723 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500724 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500725 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700726 except VppTransportShmemIOError:
727 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
728 "Cannot log show commands.")
729 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100730 else:
731 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200732
Damjan Marionf56b77a2016-10-03 19:44:57 +0200733 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200734 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800735 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200736 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100737 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400738
739 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
740 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100741 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100742 self.vpp_stdout_deque.append(
743 "--- test setUp() for %s.%s(%s) starts here ---\n" %
744 (self.__class__.__name__, self._testMethodName,
745 self._testMethodDoc))
746 self.vpp_stderr_deque.append(
747 "--- test setUp() for %s.%s(%s) starts here ---\n" %
748 (self.__class__.__name__, self._testMethodName,
749 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200750 self.vapi.cli("clear trace")
751 # store the test instance inside the test class - so that objects
752 # holding the class can access instance methods (like assertEqual)
753 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200754
Damjan Marionf56b77a2016-10-03 19:44:57 +0200755 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200756 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200757 """
758 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200759
Klement Sekera75e7d132017-09-20 08:26:30 +0200760 :param interfaces: iterable interface indexes (if None,
761 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200762
Klement Sekeraf62ae122016-10-11 11:47:09 +0200763 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200764 if interfaces is None:
765 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200766 for i in interfaces:
767 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200768
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100770 def register_capture(cls, cap_name):
771 """ Register a capture in the testclass """
772 # add to the list of captures with current timestamp
773 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100774
775 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000776 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400777 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
778 # returns float("2.190522")
779 timestr = cls.vapi.cli('show clock')
780 head, sep, tail = timestr.partition(',')
781 head, sep, tail = head.partition('Time now')
782 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000783
784 @classmethod
785 def sleep_on_vpp_time(cls, sec):
786 """ Sleep according to time in VPP world """
787 # On a busy system with many processes
788 # we might end up with VPP time being slower than real world
789 # So take that into account when waiting for VPP to do something
790 start_time = cls.get_vpp_time()
791 while cls.get_vpp_time() - start_time < sec:
792 cls.sleep(0.1)
793
794 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100795 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000796 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100797 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200798 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000799 # PG, when starts, runs to completion -
800 # so let's avoid a race condition,
801 # and wait a little till it's done.
802 # Then clean it up - and then be gone.
803 deadline = time.time() + 300
804 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
805 cls.sleep(0.01) # yield
806 if time.time() > deadline:
807 cls.logger.error("Timeout waiting for pg to stop")
808 break
809 for stamp, cap_name in cls._captures:
810 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100811 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200812
Damjan Marionf56b77a2016-10-03 19:44:57 +0200813 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200814 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200815 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100816 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200817
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100818 :param interfaces: iterable indexes of the interfaces.
819 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200820
Klement Sekeraf62ae122016-10-11 11:47:09 +0200821 """
822 result = []
823 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200824 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200825 setattr(cls, intf.name, intf)
826 result.append(intf)
827 cls.pg_interfaces = result
828 return result
829
Matej Klotton0178d522016-11-04 11:11:44 +0100830 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200831 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100832 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100833 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100834
Klement Sekerab9ef2732018-06-24 22:49:33 +0200835 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100836 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100837 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200838 result = [VppLoInterface(cls) for i in range(count)]
839 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100840 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100841 cls.lo_interfaces = result
842 return result
843
Neale Ranns192b13f2019-03-15 02:16:20 -0700844 @classmethod
845 def create_bvi_interfaces(cls, count):
846 """
847 Create BVI interfaces.
848
849 :param count: number of interfaces created.
850 :returns: List of created interfaces.
851 """
852 result = [VppBviInterface(cls) for i in range(count)]
853 for intf in result:
854 setattr(cls, intf.name, intf)
855 cls.bvi_interfaces = result
856 return result
857
Damjan Marionf56b77a2016-10-03 19:44:57 +0200858 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200859 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200860 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200861 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200862 NOTE: Currently works only when Raw layer is present.
863
864 :param packet: packet
865 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200866 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200867
868 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200869 packet_len = len(packet) + 4
870 extend = size - packet_len
871 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200872 num = (extend // len(padding)) + 1
873 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200874
Klement Sekeradab231a2016-12-21 08:50:14 +0100875 @classmethod
876 def reset_packet_infos(cls):
877 """ Reset the list of packet info objects and packet counts to zero """
878 cls._packet_infos = {}
879 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200880
Klement Sekeradab231a2016-12-21 08:50:14 +0100881 @classmethod
882 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200883 """
884 Create packet info object containing the source and destination indexes
885 and add it to the testcase's packet info list
886
Klement Sekeradab231a2016-12-21 08:50:14 +0100887 :param VppInterface src_if: source interface
888 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200889
890 :returns: _PacketInfo object
891
892 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200893 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100894 info.index = len(cls._packet_infos)
895 info.src = src_if.sw_if_index
896 info.dst = dst_if.sw_if_index
897 if isinstance(dst_if, VppSubInterface):
898 dst_idx = dst_if.parent.sw_if_index
899 else:
900 dst_idx = dst_if.sw_if_index
901 if dst_idx in cls._packet_count_for_dst_if_idx:
902 cls._packet_count_for_dst_if_idx[dst_idx] += 1
903 else:
904 cls._packet_count_for_dst_if_idx[dst_idx] = 1
905 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200906 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200907
Damjan Marionf56b77a2016-10-03 19:44:57 +0200908 @staticmethod
909 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200910 """
911 Convert _PacketInfo object to packet payload
912
913 :param info: _PacketInfo object
914
915 :returns: string containing serialized data from packet info
916 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100917 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
918 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200919
Damjan Marionf56b77a2016-10-03 19:44:57 +0200920 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800921 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200922 """
923 Convert packet payload to _PacketInfo object
924
925 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700926 :type payload: <class 'scapy.packet.Raw'>
927 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800928 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700929 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200930 :returns: _PacketInfo object containing de-serialized data from payload
931
932 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800933 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200934 info = _PacketInfo()
935 info.index = int(numbers[0])
936 info.src = int(numbers[1])
937 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100938 info.ip = int(numbers[3])
939 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200940 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200941
Damjan Marionf56b77a2016-10-03 19:44:57 +0200942 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200943 """
944 Iterate over the packet info list stored in the testcase
945 Start iteration with first element if info is None
946 Continue based on index in info if info is specified
947
948 :param info: info or None
949 :returns: next info in list or None if no more infos
950 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200951 if info is None:
952 next_index = 0
953 else:
954 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100955 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200956 return None
957 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100958 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200959
Klement Sekeraf62ae122016-10-11 11:47:09 +0200960 def get_next_packet_info_for_interface(self, src_index, info):
961 """
962 Search the packet info list for the next packet info with same source
963 interface index
964
965 :param src_index: source interface index to search for
966 :param info: packet info - where to start the search
967 :returns: packet info or None
968
969 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200970 while True:
971 info = self.get_next_packet_info(info)
972 if info is None:
973 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200974 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200975 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200976
Klement Sekeraf62ae122016-10-11 11:47:09 +0200977 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
978 """
979 Search the packet info list for the next packet info with same source
980 and destination interface indexes
981
982 :param src_index: source interface index to search for
983 :param dst_index: destination interface index to search for
984 :param info: packet info - where to start the search
985 :returns: packet info or None
986
987 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200988 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200989 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200990 if info is None:
991 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200992 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200993 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200994
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200995 def assert_equal(self, real_value, expected_value, name_or_class=None):
996 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100997 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200998 return
999 try:
1000 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1001 msg = msg % (getdoc(name_or_class).strip(),
1002 real_value, str(name_or_class(real_value)),
1003 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +01001004 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001005 msg = "Invalid %s: %s does not match expected value %s" % (
1006 name_or_class, real_value, expected_value)
1007
1008 self.assertEqual(real_value, expected_value, msg)
1009
Klement Sekerab17dd962017-01-09 07:43:48 +01001010 def assert_in_range(self,
1011 real_value,
1012 expected_min,
1013 expected_max,
1014 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001015 if name is None:
1016 msg = None
1017 else:
1018 msg = "Invalid %s: %s out of range <%s,%s>" % (
1019 name, real_value, expected_min, expected_max)
1020 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1021
Klement Sekerad81ae412018-05-16 10:52:54 +02001022 def assert_packet_checksums_valid(self, packet,
1023 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001024 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001025 udp_layers = ['UDP', 'UDPerror']
1026 checksum_fields = ['cksum', 'chksum']
1027 checksums = []
1028 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001029 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001030 while True:
1031 layer = temp.getlayer(counter)
1032 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001033 layer = layer.copy()
1034 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001035 for cf in checksum_fields:
1036 if hasattr(layer, cf):
1037 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001038 0 == getattr(layer, cf) and \
1039 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001040 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001041 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001042 checksums.append((counter, cf))
1043 else:
1044 break
1045 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001046 if 0 == len(checksums):
1047 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001048 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001049 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001050 calc_sum = getattr(temp[layer], cf)
1051 self.assert_equal(
1052 getattr(received[layer], cf), calc_sum,
1053 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1054 self.logger.debug(
1055 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1056 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001057
1058 def assert_checksum_valid(self, received_packet, layer,
1059 field_name='chksum',
1060 ignore_zero_checksum=False):
1061 """ Check checksum of received packet on given layer """
1062 received_packet_checksum = getattr(received_packet[layer], field_name)
1063 if ignore_zero_checksum and 0 == received_packet_checksum:
1064 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001065 recalculated = received_packet.__class__(
1066 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001067 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001068 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001069 self.assert_equal(received_packet_checksum,
1070 getattr(recalculated[layer], field_name),
1071 "packet checksum on layer: %s" % layer)
1072
1073 def assert_ip_checksum_valid(self, received_packet,
1074 ignore_zero_checksum=False):
1075 self.assert_checksum_valid(received_packet, 'IP',
1076 ignore_zero_checksum=ignore_zero_checksum)
1077
1078 def assert_tcp_checksum_valid(self, received_packet,
1079 ignore_zero_checksum=False):
1080 self.assert_checksum_valid(received_packet, 'TCP',
1081 ignore_zero_checksum=ignore_zero_checksum)
1082
1083 def assert_udp_checksum_valid(self, received_packet,
1084 ignore_zero_checksum=True):
1085 self.assert_checksum_valid(received_packet, 'UDP',
1086 ignore_zero_checksum=ignore_zero_checksum)
1087
1088 def assert_embedded_icmp_checksum_valid(self, received_packet):
1089 if received_packet.haslayer(IPerror):
1090 self.assert_checksum_valid(received_packet, 'IPerror')
1091 if received_packet.haslayer(TCPerror):
1092 self.assert_checksum_valid(received_packet, 'TCPerror')
1093 if received_packet.haslayer(UDPerror):
1094 self.assert_checksum_valid(received_packet, 'UDPerror',
1095 ignore_zero_checksum=True)
1096 if received_packet.haslayer(ICMPerror):
1097 self.assert_checksum_valid(received_packet, 'ICMPerror')
1098
1099 def assert_icmp_checksum_valid(self, received_packet):
1100 self.assert_checksum_valid(received_packet, 'ICMP')
1101 self.assert_embedded_icmp_checksum_valid(received_packet)
1102
1103 def assert_icmpv6_checksum_valid(self, pkt):
1104 if pkt.haslayer(ICMPv6DestUnreach):
1105 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1106 self.assert_embedded_icmp_checksum_valid(pkt)
1107 if pkt.haslayer(ICMPv6EchoRequest):
1108 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1109 if pkt.haslayer(ICMPv6EchoReply):
1110 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1111
Klement Sekera3a343d42019-05-16 14:35:46 +02001112 def get_packet_counter(self, counter):
1113 if counter.startswith("/"):
1114 counter_value = self.statistics.get_counter(counter)
1115 else:
1116 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001117 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001118 for i in range(1, len(counters) - 1):
1119 results = counters[i].split()
1120 if results[1] == counter:
1121 counter_value = int(results[0])
1122 break
1123 return counter_value
1124
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001125 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001126 counter_value = self.get_packet_counter(counter)
1127 self.assert_equal(counter_value, expected_value,
1128 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001129
Ole Troan233e4682019-05-16 15:01:34 +02001130 def assert_error_counter_equal(self, counter, expected_value):
1131 counter_value = self.statistics.get_err_counter(counter)
1132 self.assert_equal(counter_value, expected_value,
1133 "error counter `%s'" % counter)
1134
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001135 @classmethod
1136 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001137
1138 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1139 # * by Guido, only the main thread can be interrupted.
1140 # */
1141 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1142 if timeout == 0:
1143 # yield quantum
1144 if hasattr(os, 'sched_yield'):
1145 os.sched_yield()
1146 else:
1147 time.sleep(0)
1148 return
1149
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001150 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001151 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001152 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001153 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001154 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001155 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001156 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001157 "slept for %es instead of ~%es!",
1158 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001159 if hasattr(cls, 'logger'):
1160 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001161 "Finished sleep (%s) - slept %es (wanted %es)",
1162 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001163
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001164 def pg_send(self, intf, pkts, worker=None):
Neale Ranns52fae862018-01-08 04:41:42 -08001165 self.vapi.cli("clear trace")
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001166 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001167 self.pg_enable_capture(self.pg_interfaces)
1168 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001169
1170 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1171 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001172 if not timeout:
1173 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001174 for i in self.pg_interfaces:
1175 i.get_capture(0, timeout=timeout)
1176 i.assert_nothing_captured(remark=remark)
1177 timeout = 0.1
1178
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001179 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None):
Neale Rannsd7603d92019-03-28 08:56:10 +00001180 if not n_rx:
1181 n_rx = len(pkts)
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001182 self.pg_send(intf, pkts, worker=worker)
Neale Rannsd7603d92019-03-28 08:56:10 +00001183 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001184 return rx
1185
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001186 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1187 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001188 rx = output.get_capture(len(pkts))
1189 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001190 if not timeout:
1191 timeout = 1
1192 for i in self.pg_interfaces:
1193 if i not in outputs:
1194 i.get_capture(0, timeout=timeout)
1195 i.assert_nothing_captured()
1196 timeout = 0.1
1197
Neale Ranns52fae862018-01-08 04:41:42 -08001198 return rx
1199
Damjan Marionf56b77a2016-10-03 19:44:57 +02001200
juraj.linkes184870a2018-07-16 14:22:01 +02001201def get_testcase_doc_name(test):
1202 return getdoc(test.__class__).splitlines()[0]
1203
1204
Ole Trøan5ba91592018-11-22 10:01:09 +00001205def get_test_description(descriptions, test):
1206 short_description = test.shortDescription()
1207 if descriptions and short_description:
1208 return short_description
1209 else:
1210 return str(test)
1211
1212
juraj.linkes40dd73b2018-09-21 13:55:16 +02001213class TestCaseInfo(object):
1214 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1215 self.logger = logger
1216 self.tempdir = tempdir
1217 self.vpp_pid = vpp_pid
1218 self.vpp_bin_path = vpp_bin_path
1219 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001220
1221
Damjan Marionf56b77a2016-10-03 19:44:57 +02001222class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001223 """
1224 @property result_string
1225 String variable to store the test case result string.
1226 @property errors
1227 List variable containing 2-tuples of TestCase instances and strings
1228 holding formatted tracebacks. Each tuple represents a test which
1229 raised an unexpected exception.
1230 @property failures
1231 List variable containing 2-tuples of TestCase instances and strings
1232 holding formatted tracebacks. Each tuple represents a test where
1233 a failure was explicitly signalled using the TestCase.assert*()
1234 methods.
1235 """
1236
juraj.linkes40dd73b2018-09-21 13:55:16 +02001237 failed_test_cases_info = set()
1238 core_crash_test_cases_info = set()
1239 current_test_case_info = None
1240
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001241 def __init__(self, stream=None, descriptions=None, verbosity=None,
1242 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001243 """
Klement Sekerada505f62017-01-04 12:58:53 +01001244 :param stream File descriptor to store where to report test results.
1245 Set to the standard error stream by default.
1246 :param descriptions Boolean variable to store information if to use
1247 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001248 :param verbosity Integer variable to store required verbosity level.
1249 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001250 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001251 self.stream = stream
1252 self.descriptions = descriptions
1253 self.verbosity = verbosity
1254 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001255 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001256
Damjan Marionf56b77a2016-10-03 19:44:57 +02001257 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001258 """
1259 Record a test succeeded result
1260
1261 :param test:
1262
1263 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001264 if self.current_test_case_info:
1265 self.current_test_case_info.logger.debug(
1266 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1267 test._testMethodName,
1268 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001269 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001270 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001271
juraj.linkescae64f82018-09-19 15:01:47 +02001272 self.send_result_through_pipe(test, PASS)
1273
Klement Sekeraf62ae122016-10-11 11:47:09 +02001274 def addSkip(self, test, reason):
1275 """
1276 Record a test skipped.
1277
1278 :param test:
1279 :param reason:
1280
1281 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001282 if self.current_test_case_info:
1283 self.current_test_case_info.logger.debug(
1284 "--- addSkip() %s.%s(%s) called, reason is %s" %
1285 (test.__class__.__name__, test._testMethodName,
1286 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001287 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001288 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001289
juraj.linkescae64f82018-09-19 15:01:47 +02001290 self.send_result_through_pipe(test, SKIP)
1291
juraj.linkes40dd73b2018-09-21 13:55:16 +02001292 def symlink_failed(self):
1293 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001294 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001295 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001296 link_path = os.path.join(
1297 failed_dir,
1298 '%s-FAILED' %
1299 os.path.basename(self.current_test_case_info.tempdir))
1300 if self.current_test_case_info.logger:
1301 self.current_test_case_info.logger.debug(
1302 "creating a link to the failed test")
1303 self.current_test_case_info.logger.debug(
1304 "os.symlink(%s, %s)" %
1305 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001306 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001307 if self.current_test_case_info.logger:
1308 self.current_test_case_info.logger.debug(
1309 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001310 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001311 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001312
Klement Sekeraf413bef2017-08-15 07:09:02 +02001313 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001314 if self.current_test_case_info.logger:
1315 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001316
juraj.linkescae64f82018-09-19 15:01:47 +02001317 def send_result_through_pipe(self, test, result):
1318 if hasattr(self, 'test_framework_result_pipe'):
1319 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001320 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001321 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001322
juraj.linkes40dd73b2018-09-21 13:55:16 +02001323 def log_error(self, test, err, fn_name):
1324 if self.current_test_case_info:
1325 if isinstance(test, unittest.suite._ErrorHolder):
1326 test_name = test.description
1327 else:
1328 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1329 test._testMethodName,
1330 test._testMethodDoc)
1331 self.current_test_case_info.logger.debug(
1332 "--- %s() %s called, err is %s" %
1333 (fn_name, test_name, err))
1334 self.current_test_case_info.logger.debug(
1335 "formatted exception is:\n%s" %
1336 "".join(format_exception(*err)))
1337
1338 def add_error(self, test, err, unittest_fn, error_type):
1339 if error_type == FAIL:
1340 self.log_error(test, err, 'addFailure')
1341 error_type_str = colorize("FAIL", RED)
1342 elif error_type == ERROR:
1343 self.log_error(test, err, 'addError')
1344 error_type_str = colorize("ERROR", RED)
1345 else:
1346 raise Exception('Error type %s cannot be used to record an '
1347 'error or a failure' % error_type)
1348
1349 unittest_fn(self, test, err)
1350 if self.current_test_case_info:
1351 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1352 (error_type_str,
1353 self.current_test_case_info.tempdir)
1354 self.symlink_failed()
1355 self.failed_test_cases_info.add(self.current_test_case_info)
1356 if is_core_present(self.current_test_case_info.tempdir):
1357 if not self.current_test_case_info.core_crash_test:
1358 if isinstance(test, unittest.suite._ErrorHolder):
1359 test_name = str(test)
1360 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001361 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001362 get_testcase_doc_name(test), test.id())
1363 self.current_test_case_info.core_crash_test = test_name
1364 self.core_crash_test_cases_info.add(
1365 self.current_test_case_info)
1366 else:
1367 self.result_string = '%s [no temp dir]' % error_type_str
1368
1369 self.send_result_through_pipe(test, error_type)
1370
Damjan Marionf56b77a2016-10-03 19:44:57 +02001371 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001372 """
1373 Record a test failed result
1374
1375 :param test:
1376 :param err: error message
1377
1378 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001379 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001380
Damjan Marionf56b77a2016-10-03 19:44:57 +02001381 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001382 """
1383 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001384
Klement Sekeraf62ae122016-10-11 11:47:09 +02001385 :param test:
1386 :param err: error message
1387
1388 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001389 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001390
Damjan Marionf56b77a2016-10-03 19:44:57 +02001391 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001392 """
1393 Get test description
1394
1395 :param test:
1396 :returns: test description
1397
1398 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001399 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001400
Damjan Marionf56b77a2016-10-03 19:44:57 +02001401 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001402 """
1403 Start a test
1404
1405 :param test:
1406
1407 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001408
1409 def print_header(test):
1410 if not hasattr(test.__class__, '_header_printed'):
1411 print(double_line_delim)
1412 print(colorize(getdoc(test).splitlines()[0], GREEN))
1413 print(double_line_delim)
1414 test.__class__._header_printed = True
1415
1416 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001417 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001418 unittest.TestResult.startTest(self, test)
1419 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001420 self.stream.writeln(
1421 "Starting " + self.getDescription(test) + " ...")
1422 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001423
Damjan Marionf56b77a2016-10-03 19:44:57 +02001424 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001425 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001426 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001427
1428 :param test:
1429
1430 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001431 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001432
Damjan Marionf56b77a2016-10-03 19:44:57 +02001433 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001434 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001435 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001436 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001437 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001438 else:
Ole Troan0c629322019-11-28 14:48:44 +01001439 self.stream.writeln("%-68s %4.2f %s" %
1440 (self.getDescription(test),
1441 time.time() - self.start_test,
1442 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001443
1444 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001445
Damjan Marionf56b77a2016-10-03 19:44:57 +02001446 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001447 """
1448 Print errors from running the test case
1449 """
juraj.linkesabec0122018-11-16 17:28:56 +01001450 if len(self.errors) > 0 or len(self.failures) > 0:
1451 self.stream.writeln()
1452 self.printErrorList('ERROR', self.errors)
1453 self.printErrorList('FAIL', self.failures)
1454
1455 # ^^ that is the last output from unittest before summary
1456 if not self.runner.print_summary:
1457 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1458 self.stream = devnull
1459 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001460
Damjan Marionf56b77a2016-10-03 19:44:57 +02001461 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001462 """
1463 Print error list to the output stream together with error type
1464 and test case description.
1465
1466 :param flavour: error type
1467 :param errors: iterable errors
1468
1469 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001470 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001471 self.stream.writeln(double_line_delim)
1472 self.stream.writeln("%s: %s" %
1473 (flavour, self.getDescription(test)))
1474 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001475 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001476
1477
Damjan Marionf56b77a2016-10-03 19:44:57 +02001478class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001479 """
Klement Sekera104543f2017-02-03 07:29:43 +01001480 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001481 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001482
Klement Sekeraf62ae122016-10-11 11:47:09 +02001483 @property
1484 def resultclass(self):
1485 """Class maintaining the results of the tests"""
1486 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001487
juraj.linkes184870a2018-07-16 14:22:01 +02001488 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001489 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001490 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001491 # ignore stream setting here, use hard-coded stdout to be in sync
1492 # with prints from VppTestCase methods ...
1493 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1494 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001495 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001496 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001497
juraj.linkesabec0122018-11-16 17:28:56 +01001498 self.orig_stream = self.stream
1499 self.resultclass.test_framework_result_pipe = result_pipe
1500
1501 self.print_summary = print_summary
1502
1503 def _makeResult(self):
1504 return self.resultclass(self.stream,
1505 self.descriptions,
1506 self.verbosity,
1507 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001508
Damjan Marionf56b77a2016-10-03 19:44:57 +02001509 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001510 """
1511 Run the tests
1512
1513 :param test:
1514
1515 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001516 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001517
1518 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001519 if not self.print_summary:
1520 self.stream = self.orig_stream
1521 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001522 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001523
1524
1525class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001526 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1527 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001528 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001529 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001530 if hasattr(self, 'testcase') and self.testcase.debug_all:
1531 if self.testcase.debug_gdbserver:
1532 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1533 .format(port=self.testcase.gdbserver_port)] + args
1534 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1535 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001536 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001537 self.app_name = os.path.basename(self.app_bin)
1538 if hasattr(self, 'role'):
1539 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001540 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001541 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001542 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001543 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001544
Dave Wallace24564332019-10-21 02:53:14 +00001545 def wait_for_enter(self):
1546 if not hasattr(self, 'testcase'):
1547 return
1548 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1549 print()
1550 print(double_line_delim)
1551 print("Spawned GDB Server for '{app}' with PID: {pid}"
1552 .format(app=self.app_name, pid=self.process.pid))
1553 elif self.testcase.debug_all and self.testcase.debug_gdb:
1554 print()
1555 print(double_line_delim)
1556 print("Spawned '{app}' with PID: {pid}"
1557 .format(app=self.app_name, pid=self.process.pid))
1558 else:
1559 return
1560 print(single_line_delim)
1561 print("You can debug '{app}' using:".format(app=self.app_name))
1562 if self.testcase.debug_gdbserver:
1563 print("sudo gdb " + self.app_bin +
1564 " -ex 'target remote localhost:{port}'"
1565 .format(port=self.testcase.gdbserver_port))
1566 print("Now is the time to attach gdb by running the above "
1567 "command, set up breakpoints etc., then resume from "
1568 "within gdb by issuing the 'continue' command")
1569 self.testcase.gdbserver_port += 1
1570 elif self.testcase.debug_gdb:
1571 print("sudo gdb " + self.app_bin +
1572 " -ex 'attach {pid}'".format(pid=self.process.pid))
1573 print("Now is the time to attach gdb by running the above "
1574 "command and set up breakpoints etc., then resume from"
1575 " within gdb by issuing the 'continue' command")
1576 print(single_line_delim)
1577 input("Press ENTER to continue running the testcase...")
1578
Neale Ranns812ed392017-10-16 04:20:13 -07001579 def run(self):
1580 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001581 if not os.path.exists(executable) or not os.access(
1582 executable, os.F_OK | os.X_OK):
1583 # Exit code that means some system file did not exist,
1584 # could not be opened, or had some other kind of error.
1585 self.result = os.EX_OSFILE
1586 raise EnvironmentError(
1587 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001588 self.logger.debug("Running executable: '{app}'"
1589 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001590 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001591 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001592 env["CK_LOG_FILE_NAME"] = "-"
1593 self.process = subprocess.Popen(
1594 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1595 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001596 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001597 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001598 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001599 self.logger.info("Return code is `%s'" % self.process.returncode)
1600 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001601 self.logger.info("Executable `{app}' wrote to stdout:"
1602 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001603 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001604 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001605 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001606 self.logger.info("Executable `{app}' wrote to stderr:"
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(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001610 self.logger.info(single_line_delim)
1611 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001612
Klement Sekera6aa58b72019-05-16 14:34:55 +02001613
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001614if __name__ == '__main__':
1615 pass