blob: c21d1882be761d7cf3be05e6d1db74a422a77a12 [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
Ole Troana45dc072018-12-21 16:04:22 +0100384 cls.vpp_cmdline = [cls.vpp_bin, "unix",
385 "{", "nodaemon", debug_cli, "full-coredump",
386 coredump_size, "runtime-dir", cls.tempdir, "}",
387 "api-trace", "{", "on", "}", "api-segment", "{",
388 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000389 "main-core", str(cpu_core_number),
390 cls.worker_config, "}",
Dave Barach4ed25982019-12-25 09:24:58 -0500391 "physmem", "{", "max-size", "32m", "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200392 "statseg", "{", "socket-name", cls.stats_sock, "}",
393 "socksvr", "{", "socket-name", cls.api_sock, "}",
394 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100395 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200396 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100397 "}", "plugin", "unittest_plugin.so", "{", "enable",
398 "}"] + cls.extra_vpp_plugin_config + ["}", ]
399 if cls.extra_vpp_punt_config is not None:
400 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100401 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100402 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400403 if cls.test_plugin_path is not None:
404 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
405
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100406 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
407 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200408
409 @classmethod
410 def wait_for_enter(cls):
411 if cls.debug_gdbserver:
412 print(double_line_delim)
413 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
414 elif cls.debug_gdb:
415 print(double_line_delim)
416 print("Spawned VPP with PID: %d" % cls.vpp.pid)
417 else:
418 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
419 return
420 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000421 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200422 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400423 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000424 " -ex 'target remote localhost:{port}'"
425 .format(port=cls.gdbserver_port))
426 print("Now is the time to attach gdb by running the above "
427 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200428 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000429 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200430 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400431 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000432 print("Now is the time to attach gdb by running the above "
433 "command and set up breakpoints etc., then resume VPP from"
434 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200435 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800436 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200437
438 @classmethod
439 def run_vpp(cls):
440 cmdline = cls.vpp_cmdline
441
442 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100443 gdbserver = '/usr/bin/gdbserver'
444 if not os.path.isfile(gdbserver) or \
445 not os.access(gdbserver, os.X_OK):
446 raise Exception("gdbserver binary '%s' does not exist or is "
447 "not executable" % gdbserver)
448
Dave Wallace24564332019-10-21 02:53:14 +0000449 cmdline = [gdbserver, 'localhost:{port}'
450 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200451 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
452
Klement Sekera931be3a2016-11-03 05:36:01 +0100453 try:
454 cls.vpp = subprocess.Popen(cmdline,
455 stdout=subprocess.PIPE,
Ole Troan6e6ad642020-02-04 13:28:13 +0100456 stderr=subprocess.PIPE)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800457 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800458 cls.logger.critical("Subprocess returned with non-0 return code: ("
459 "%s)", e.returncode)
460 raise
461 except OSError as e:
462 cls.logger.critical("Subprocess returned with OS error: "
463 "(%s) %s", e.errno, e.strerror)
464 raise
465 except Exception as e:
466 cls.logger.exception("Subprocess returned unexpected from "
467 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100468 raise
469
Klement Sekera277b89c2016-10-28 13:20:27 +0200470 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100471
Damjan Marionf56b77a2016-10-03 19:44:57 +0200472 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000473 def wait_for_coredump(cls):
474 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400475 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000476 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400477 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000478 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400479 ok = False
480 while time.time() < deadline:
481 cls.sleep(1)
482 size = curr_size
483 curr_size = os.path.getsize(corefile)
484 if size == curr_size:
485 ok = True
486 break
487 if not ok:
488 cls.logger.error("Timed out waiting for coredump to complete:"
489 " %s", corefile)
490 else:
491 cls.logger.error("Coredump complete: %s, size %d",
492 corefile, curr_size)
493
494 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200495 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200496 """
497 Perform class setup before running the testcase
498 Remove shared memory files, start vpp and connect the vpp-api
499 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800500 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100501 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100502 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000503 seed = os.environ["RND_SEED"]
504 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100505 if hasattr(cls, 'parallel_handler'):
506 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100507 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700508
Klement Sekeraf62ae122016-10-11 11:47:09 +0200509 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200510 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200511 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200512 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200513 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
514 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100515 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
516 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200517 cls.file_handler.setLevel(DEBUG)
518 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700519 cls.logger.debug("--- setUpClass() for %s called ---" %
520 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200521 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200522 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200523 cls.logger.info("Temporary dir is %s, shm prefix is %s",
524 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000525 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200526 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100527 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100528 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200529 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100530 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100531 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200532 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200533 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200534 # need to catch exceptions here because if we raise, then the cleanup
535 # doesn't get called and we might end with a zombie vpp
536 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200537 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200538 cls.reporter.send_keep_alive(cls, 'setUpClass')
539 VppTestResult.current_test_case_info = TestCaseInfo(
540 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100541 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100542 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100543 cls.pump_thread_stop_flag = Event()
544 cls.pump_thread_wakeup_pipe = os.pipe()
545 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100546 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100547 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200548 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400549 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200550 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400551 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100552 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400553 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100554 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400555 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100556 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200557 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200558 try:
559 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100560 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200561 cls.vpp_startup_failed = True
562 cls.logger.critical(
563 "VPP died shortly after startup, check the"
564 " output to standard error for possible cause")
565 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100566 try:
567 cls.vapi.connect()
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500568 except vpp_papi.VPPIOError as e:
569 cls.logger.debug("Exception connecting to vapi: %s" % e)
570 cls.vapi.disconnect()
571
Klement Sekera085f5c02016-11-24 01:59:16 +0100572 if cls.debug_gdbserver:
573 print(colorize("You're running VPP inside gdbserver but "
574 "VPP-API connection failed, did you forget "
575 "to 'continue' VPP from within gdb?", RED))
576 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000577 except Exception as e:
578 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400579
580 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100581 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200582
Damjan Marionf56b77a2016-10-03 19:44:57 +0200583 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500584 def _debug_quit(cls):
585 if (cls.debug_gdbserver or cls.debug_gdb):
586 try:
587 cls.vpp.poll()
588
589 if cls.vpp.returncode is None:
590 print()
591 print(double_line_delim)
592 print("VPP or GDB server is still running")
593 print(single_line_delim)
594 input("When done debugging, press ENTER to kill the "
595 "process and finish running the testcase...")
596 except AttributeError:
597 pass
598
599 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200600 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200601 """
602 Disconnect vpp-api, kill vpp and cleanup shared memory files
603 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500604 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200605
juraj.linkes184870a2018-07-16 14:22:01 +0200606 # first signal that we want to stop the pump thread, then wake it up
607 if hasattr(cls, 'pump_thread_stop_flag'):
608 cls.pump_thread_stop_flag.set()
609 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100610 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100611 if hasattr(cls, 'pump_thread'):
612 cls.logger.debug("Waiting for pump thread to stop")
613 cls.pump_thread.join()
614 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500615 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100616 cls.vpp_stderr_reader_thread.join()
617
Klement Sekeraf62ae122016-10-11 11:47:09 +0200618 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100619 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100620 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700621 cls.logger.debug("Disconnecting class vapi client on %s",
622 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100623 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700624 cls.logger.debug("Deleting class vapi attribute on %s",
625 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100626 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200627 cls.vpp.poll()
628 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000629 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100630 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400631 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100632 cls.logger.debug("Waiting for vpp to die")
633 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700634 cls.logger.debug("Deleting class vpp attribute on %s",
635 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200636 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200637
Klement Sekera3747c752017-04-10 06:30:17 +0200638 if cls.vpp_startup_failed:
639 stdout_log = cls.logger.info
640 stderr_log = cls.logger.critical
641 else:
642 stdout_log = cls.logger.info
643 stderr_log = cls.logger.info
644
Klement Sekerae4504c62016-12-08 10:16:41 +0100645 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200646 stdout_log(single_line_delim)
647 stdout_log('VPP output to stdout while running %s:', cls.__name__)
648 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100649 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200650 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
651 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200652 stdout_log('\n%s', vpp_output)
653 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200654
Klement Sekerae4504c62016-12-08 10:16:41 +0100655 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200656 stderr_log(single_line_delim)
657 stderr_log('VPP output to stderr while running %s:', cls.__name__)
658 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100659 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200660 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
661 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200662 stderr_log('\n%s', vpp_output)
663 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200664
Damjan Marionf56b77a2016-10-03 19:44:57 +0200665 @classmethod
666 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200667 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700668 cls.logger.debug("--- tearDownClass() for %s called ---" %
669 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200670 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200671 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200672 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100673 cls.reset_packet_infos()
674 if debug_framework:
675 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200676
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700677 def show_commands_at_teardown(self):
678 """ Allow subclass specific teardown logging additions."""
679 self.logger.info("--- No test specific show commands provided. ---")
680
Damjan Marionf56b77a2016-10-03 19:44:57 +0200681 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200682 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100683 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
684 (self.__class__.__name__, self._testMethodName,
685 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700686
687 try:
688 if not self.vpp_dead:
689 self.logger.debug(self.vapi.cli("show trace max 1000"))
690 self.logger.info(self.vapi.ppcli("show interface"))
691 self.logger.info(self.vapi.ppcli("show hardware"))
692 self.logger.info(self.statistics.set_errors_str())
693 self.logger.info(self.vapi.ppcli("show run"))
694 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400695 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700696 self.logger.info("Logging testcase specific show commands.")
697 self.show_commands_at_teardown()
698 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500699 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000700 m = self._testMethodName
701 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500702 tmp_api_trace = "/tmp/%s" % api_trace
703 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
704 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
705 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
706 vpp_api_trace_log))
707 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500708 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500709 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700710 except VppTransportShmemIOError:
711 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
712 "Cannot log show commands.")
713 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100714 else:
715 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200716
Damjan Marionf56b77a2016-10-03 19:44:57 +0200717 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200718 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800719 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200720 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100721 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400722
723 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
724 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100725 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100726 self.vpp_stdout_deque.append(
727 "--- test setUp() for %s.%s(%s) starts here ---\n" %
728 (self.__class__.__name__, self._testMethodName,
729 self._testMethodDoc))
730 self.vpp_stderr_deque.append(
731 "--- test setUp() for %s.%s(%s) starts here ---\n" %
732 (self.__class__.__name__, self._testMethodName,
733 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200734 self.vapi.cli("clear trace")
735 # store the test instance inside the test class - so that objects
736 # holding the class can access instance methods (like assertEqual)
737 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200738
Damjan Marionf56b77a2016-10-03 19:44:57 +0200739 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200740 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200741 """
742 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200743
Klement Sekera75e7d132017-09-20 08:26:30 +0200744 :param interfaces: iterable interface indexes (if None,
745 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200746
Klement Sekeraf62ae122016-10-11 11:47:09 +0200747 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200748 if interfaces is None:
749 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200750 for i in interfaces:
751 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200752
Damjan Marionf56b77a2016-10-03 19:44:57 +0200753 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100754 def register_capture(cls, cap_name):
755 """ Register a capture in the testclass """
756 # add to the list of captures with current timestamp
757 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100758
759 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000760 def get_vpp_time(cls):
Dave Barach19718002020-03-11 10:31:36 -0400761 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
762 # returns float("2.190522")
763 timestr = cls.vapi.cli('show clock')
764 head, sep, tail = timestr.partition(',')
765 head, sep, tail = head.partition('Time now')
766 return float(tail)
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000767
768 @classmethod
769 def sleep_on_vpp_time(cls, sec):
770 """ Sleep according to time in VPP world """
771 # On a busy system with many processes
772 # we might end up with VPP time being slower than real world
773 # So take that into account when waiting for VPP to do something
774 start_time = cls.get_vpp_time()
775 while cls.get_vpp_time() - start_time < sec:
776 cls.sleep(0.1)
777
778 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100779 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000780 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100781 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200782 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000783 # PG, when starts, runs to completion -
784 # so let's avoid a race condition,
785 # and wait a little till it's done.
786 # Then clean it up - and then be gone.
787 deadline = time.time() + 300
788 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
789 cls.sleep(0.01) # yield
790 if time.time() > deadline:
791 cls.logger.error("Timeout waiting for pg to stop")
792 break
793 for stamp, cap_name in cls._captures:
794 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100795 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200796
Damjan Marionf56b77a2016-10-03 19:44:57 +0200797 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200798 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200799 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100800 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200801
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100802 :param interfaces: iterable indexes of the interfaces.
803 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200804
Klement Sekeraf62ae122016-10-11 11:47:09 +0200805 """
806 result = []
807 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200808 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200809 setattr(cls, intf.name, intf)
810 result.append(intf)
811 cls.pg_interfaces = result
812 return result
813
Matej Klotton0178d522016-11-04 11:11:44 +0100814 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200815 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100816 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100817 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100818
Klement Sekerab9ef2732018-06-24 22:49:33 +0200819 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100820 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100821 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200822 result = [VppLoInterface(cls) for i in range(count)]
823 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100824 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100825 cls.lo_interfaces = result
826 return result
827
Neale Ranns192b13f2019-03-15 02:16:20 -0700828 @classmethod
829 def create_bvi_interfaces(cls, count):
830 """
831 Create BVI interfaces.
832
833 :param count: number of interfaces created.
834 :returns: List of created interfaces.
835 """
836 result = [VppBviInterface(cls) for i in range(count)]
837 for intf in result:
838 setattr(cls, intf.name, intf)
839 cls.bvi_interfaces = result
840 return result
841
Damjan Marionf56b77a2016-10-03 19:44:57 +0200842 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200843 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200844 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200845 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200846 NOTE: Currently works only when Raw layer is present.
847
848 :param packet: packet
849 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200850 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200851
852 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200853 packet_len = len(packet) + 4
854 extend = size - packet_len
855 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200856 num = (extend // len(padding)) + 1
857 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200858
Klement Sekeradab231a2016-12-21 08:50:14 +0100859 @classmethod
860 def reset_packet_infos(cls):
861 """ Reset the list of packet info objects and packet counts to zero """
862 cls._packet_infos = {}
863 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200864
Klement Sekeradab231a2016-12-21 08:50:14 +0100865 @classmethod
866 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200867 """
868 Create packet info object containing the source and destination indexes
869 and add it to the testcase's packet info list
870
Klement Sekeradab231a2016-12-21 08:50:14 +0100871 :param VppInterface src_if: source interface
872 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200873
874 :returns: _PacketInfo object
875
876 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200877 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100878 info.index = len(cls._packet_infos)
879 info.src = src_if.sw_if_index
880 info.dst = dst_if.sw_if_index
881 if isinstance(dst_if, VppSubInterface):
882 dst_idx = dst_if.parent.sw_if_index
883 else:
884 dst_idx = dst_if.sw_if_index
885 if dst_idx in cls._packet_count_for_dst_if_idx:
886 cls._packet_count_for_dst_if_idx[dst_idx] += 1
887 else:
888 cls._packet_count_for_dst_if_idx[dst_idx] = 1
889 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200890 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200891
Damjan Marionf56b77a2016-10-03 19:44:57 +0200892 @staticmethod
893 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200894 """
895 Convert _PacketInfo object to packet payload
896
897 :param info: _PacketInfo object
898
899 :returns: string containing serialized data from packet info
900 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100901 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
902 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200903
Damjan Marionf56b77a2016-10-03 19:44:57 +0200904 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800905 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200906 """
907 Convert packet payload to _PacketInfo object
908
909 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700910 :type payload: <class 'scapy.packet.Raw'>
911 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800912 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700913 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200914 :returns: _PacketInfo object containing de-serialized data from payload
915
916 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800917 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200918 info = _PacketInfo()
919 info.index = int(numbers[0])
920 info.src = int(numbers[1])
921 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100922 info.ip = int(numbers[3])
923 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200924 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200925
Damjan Marionf56b77a2016-10-03 19:44:57 +0200926 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200927 """
928 Iterate over the packet info list stored in the testcase
929 Start iteration with first element if info is None
930 Continue based on index in info if info is specified
931
932 :param info: info or None
933 :returns: next info in list or None if no more infos
934 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200935 if info is None:
936 next_index = 0
937 else:
938 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100939 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200940 return None
941 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100942 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200943
Klement Sekeraf62ae122016-10-11 11:47:09 +0200944 def get_next_packet_info_for_interface(self, src_index, info):
945 """
946 Search the packet info list for the next packet info with same source
947 interface index
948
949 :param src_index: source interface index to search for
950 :param info: packet info - where to start the search
951 :returns: packet info or None
952
953 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200954 while True:
955 info = self.get_next_packet_info(info)
956 if info is None:
957 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200958 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200959 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200960
Klement Sekeraf62ae122016-10-11 11:47:09 +0200961 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
962 """
963 Search the packet info list for the next packet info with same source
964 and destination interface indexes
965
966 :param src_index: source interface index to search for
967 :param dst_index: destination interface index to search for
968 :param info: packet info - where to start the search
969 :returns: packet info or None
970
971 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200972 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200973 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200974 if info is None:
975 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200976 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200977 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200978
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200979 def assert_equal(self, real_value, expected_value, name_or_class=None):
980 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100981 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200982 return
983 try:
984 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
985 msg = msg % (getdoc(name_or_class).strip(),
986 real_value, str(name_or_class(real_value)),
987 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100988 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200989 msg = "Invalid %s: %s does not match expected value %s" % (
990 name_or_class, real_value, expected_value)
991
992 self.assertEqual(real_value, expected_value, msg)
993
Klement Sekerab17dd962017-01-09 07:43:48 +0100994 def assert_in_range(self,
995 real_value,
996 expected_min,
997 expected_max,
998 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200999 if name is None:
1000 msg = None
1001 else:
1002 msg = "Invalid %s: %s out of range <%s,%s>" % (
1003 name, real_value, expected_min, expected_max)
1004 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1005
Klement Sekerad81ae412018-05-16 10:52:54 +02001006 def assert_packet_checksums_valid(self, packet,
1007 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001008 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001009 udp_layers = ['UDP', 'UDPerror']
1010 checksum_fields = ['cksum', 'chksum']
1011 checksums = []
1012 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001013 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001014 while True:
1015 layer = temp.getlayer(counter)
1016 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001017 layer = layer.copy()
1018 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001019 for cf in checksum_fields:
1020 if hasattr(layer, cf):
1021 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001022 0 == getattr(layer, cf) and \
1023 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001024 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001025 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001026 checksums.append((counter, cf))
1027 else:
1028 break
1029 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001030 if 0 == len(checksums):
1031 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001032 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001033 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001034 calc_sum = getattr(temp[layer], cf)
1035 self.assert_equal(
1036 getattr(received[layer], cf), calc_sum,
1037 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1038 self.logger.debug(
1039 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1040 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001041
1042 def assert_checksum_valid(self, received_packet, layer,
1043 field_name='chksum',
1044 ignore_zero_checksum=False):
1045 """ Check checksum of received packet on given layer """
1046 received_packet_checksum = getattr(received_packet[layer], field_name)
1047 if ignore_zero_checksum and 0 == received_packet_checksum:
1048 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001049 recalculated = received_packet.__class__(
1050 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001051 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001052 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001053 self.assert_equal(received_packet_checksum,
1054 getattr(recalculated[layer], field_name),
1055 "packet checksum on layer: %s" % layer)
1056
1057 def assert_ip_checksum_valid(self, received_packet,
1058 ignore_zero_checksum=False):
1059 self.assert_checksum_valid(received_packet, 'IP',
1060 ignore_zero_checksum=ignore_zero_checksum)
1061
1062 def assert_tcp_checksum_valid(self, received_packet,
1063 ignore_zero_checksum=False):
1064 self.assert_checksum_valid(received_packet, 'TCP',
1065 ignore_zero_checksum=ignore_zero_checksum)
1066
1067 def assert_udp_checksum_valid(self, received_packet,
1068 ignore_zero_checksum=True):
1069 self.assert_checksum_valid(received_packet, 'UDP',
1070 ignore_zero_checksum=ignore_zero_checksum)
1071
1072 def assert_embedded_icmp_checksum_valid(self, received_packet):
1073 if received_packet.haslayer(IPerror):
1074 self.assert_checksum_valid(received_packet, 'IPerror')
1075 if received_packet.haslayer(TCPerror):
1076 self.assert_checksum_valid(received_packet, 'TCPerror')
1077 if received_packet.haslayer(UDPerror):
1078 self.assert_checksum_valid(received_packet, 'UDPerror',
1079 ignore_zero_checksum=True)
1080 if received_packet.haslayer(ICMPerror):
1081 self.assert_checksum_valid(received_packet, 'ICMPerror')
1082
1083 def assert_icmp_checksum_valid(self, received_packet):
1084 self.assert_checksum_valid(received_packet, 'ICMP')
1085 self.assert_embedded_icmp_checksum_valid(received_packet)
1086
1087 def assert_icmpv6_checksum_valid(self, pkt):
1088 if pkt.haslayer(ICMPv6DestUnreach):
1089 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1090 self.assert_embedded_icmp_checksum_valid(pkt)
1091 if pkt.haslayer(ICMPv6EchoRequest):
1092 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1093 if pkt.haslayer(ICMPv6EchoReply):
1094 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1095
Klement Sekera3a343d42019-05-16 14:35:46 +02001096 def get_packet_counter(self, counter):
1097 if counter.startswith("/"):
1098 counter_value = self.statistics.get_counter(counter)
1099 else:
1100 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001101 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001102 for i in range(1, len(counters) - 1):
1103 results = counters[i].split()
1104 if results[1] == counter:
1105 counter_value = int(results[0])
1106 break
1107 return counter_value
1108
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001109 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001110 counter_value = self.get_packet_counter(counter)
1111 self.assert_equal(counter_value, expected_value,
1112 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001113
Ole Troan233e4682019-05-16 15:01:34 +02001114 def assert_error_counter_equal(self, counter, expected_value):
1115 counter_value = self.statistics.get_err_counter(counter)
1116 self.assert_equal(counter_value, expected_value,
1117 "error counter `%s'" % counter)
1118
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001119 @classmethod
1120 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001121
1122 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1123 # * by Guido, only the main thread can be interrupted.
1124 # */
1125 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1126 if timeout == 0:
1127 # yield quantum
1128 if hasattr(os, 'sched_yield'):
1129 os.sched_yield()
1130 else:
1131 time.sleep(0)
1132 return
1133
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001134 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001135 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001136 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001137 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001138 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001139 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001140 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001141 "slept for %es instead of ~%es!",
1142 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001143 if hasattr(cls, 'logger'):
1144 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001145 "Finished sleep (%s) - slept %es (wanted %es)",
1146 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001147
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001148 def pg_send(self, intf, pkts, worker=None):
Neale Ranns52fae862018-01-08 04:41:42 -08001149 self.vapi.cli("clear trace")
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001150 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001151 self.pg_enable_capture(self.pg_interfaces)
1152 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001153
1154 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1155 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001156 if not timeout:
1157 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001158 for i in self.pg_interfaces:
1159 i.get_capture(0, timeout=timeout)
1160 i.assert_nothing_captured(remark=remark)
1161 timeout = 0.1
1162
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001163 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None):
Neale Rannsd7603d92019-03-28 08:56:10 +00001164 if not n_rx:
1165 n_rx = len(pkts)
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001166 self.pg_send(intf, pkts, worker=worker)
Neale Rannsd7603d92019-03-28 08:56:10 +00001167 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001168 return rx
1169
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001170 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1171 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001172 rx = output.get_capture(len(pkts))
1173 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001174 if not timeout:
1175 timeout = 1
1176 for i in self.pg_interfaces:
1177 if i not in outputs:
1178 i.get_capture(0, timeout=timeout)
1179 i.assert_nothing_captured()
1180 timeout = 0.1
1181
Neale Ranns52fae862018-01-08 04:41:42 -08001182 return rx
1183
Damjan Marionf56b77a2016-10-03 19:44:57 +02001184
juraj.linkes184870a2018-07-16 14:22:01 +02001185def get_testcase_doc_name(test):
1186 return getdoc(test.__class__).splitlines()[0]
1187
1188
Ole Trøan5ba91592018-11-22 10:01:09 +00001189def get_test_description(descriptions, test):
1190 short_description = test.shortDescription()
1191 if descriptions and short_description:
1192 return short_description
1193 else:
1194 return str(test)
1195
1196
juraj.linkes40dd73b2018-09-21 13:55:16 +02001197class TestCaseInfo(object):
1198 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1199 self.logger = logger
1200 self.tempdir = tempdir
1201 self.vpp_pid = vpp_pid
1202 self.vpp_bin_path = vpp_bin_path
1203 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001204
1205
Damjan Marionf56b77a2016-10-03 19:44:57 +02001206class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001207 """
1208 @property result_string
1209 String variable to store the test case result string.
1210 @property errors
1211 List variable containing 2-tuples of TestCase instances and strings
1212 holding formatted tracebacks. Each tuple represents a test which
1213 raised an unexpected exception.
1214 @property failures
1215 List variable containing 2-tuples of TestCase instances and strings
1216 holding formatted tracebacks. Each tuple represents a test where
1217 a failure was explicitly signalled using the TestCase.assert*()
1218 methods.
1219 """
1220
juraj.linkes40dd73b2018-09-21 13:55:16 +02001221 failed_test_cases_info = set()
1222 core_crash_test_cases_info = set()
1223 current_test_case_info = None
1224
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001225 def __init__(self, stream=None, descriptions=None, verbosity=None,
1226 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001227 """
Klement Sekerada505f62017-01-04 12:58:53 +01001228 :param stream File descriptor to store where to report test results.
1229 Set to the standard error stream by default.
1230 :param descriptions Boolean variable to store information if to use
1231 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001232 :param verbosity Integer variable to store required verbosity level.
1233 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001234 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001235 self.stream = stream
1236 self.descriptions = descriptions
1237 self.verbosity = verbosity
1238 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001239 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001240
Damjan Marionf56b77a2016-10-03 19:44:57 +02001241 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001242 """
1243 Record a test succeeded result
1244
1245 :param test:
1246
1247 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001248 if self.current_test_case_info:
1249 self.current_test_case_info.logger.debug(
1250 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1251 test._testMethodName,
1252 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001253 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001254 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001255
juraj.linkescae64f82018-09-19 15:01:47 +02001256 self.send_result_through_pipe(test, PASS)
1257
Klement Sekeraf62ae122016-10-11 11:47:09 +02001258 def addSkip(self, test, reason):
1259 """
1260 Record a test skipped.
1261
1262 :param test:
1263 :param reason:
1264
1265 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001266 if self.current_test_case_info:
1267 self.current_test_case_info.logger.debug(
1268 "--- addSkip() %s.%s(%s) called, reason is %s" %
1269 (test.__class__.__name__, test._testMethodName,
1270 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001271 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001272 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001273
juraj.linkescae64f82018-09-19 15:01:47 +02001274 self.send_result_through_pipe(test, SKIP)
1275
juraj.linkes40dd73b2018-09-21 13:55:16 +02001276 def symlink_failed(self):
1277 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001278 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001279 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001280 link_path = os.path.join(
1281 failed_dir,
1282 '%s-FAILED' %
1283 os.path.basename(self.current_test_case_info.tempdir))
1284 if self.current_test_case_info.logger:
1285 self.current_test_case_info.logger.debug(
1286 "creating a link to the failed test")
1287 self.current_test_case_info.logger.debug(
1288 "os.symlink(%s, %s)" %
1289 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001290 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001291 if self.current_test_case_info.logger:
1292 self.current_test_case_info.logger.debug(
1293 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001294 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001295 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001296
Klement Sekeraf413bef2017-08-15 07:09:02 +02001297 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001298 if self.current_test_case_info.logger:
1299 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001300
juraj.linkescae64f82018-09-19 15:01:47 +02001301 def send_result_through_pipe(self, test, result):
1302 if hasattr(self, 'test_framework_result_pipe'):
1303 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001304 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001305 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001306
juraj.linkes40dd73b2018-09-21 13:55:16 +02001307 def log_error(self, test, err, fn_name):
1308 if self.current_test_case_info:
1309 if isinstance(test, unittest.suite._ErrorHolder):
1310 test_name = test.description
1311 else:
1312 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1313 test._testMethodName,
1314 test._testMethodDoc)
1315 self.current_test_case_info.logger.debug(
1316 "--- %s() %s called, err is %s" %
1317 (fn_name, test_name, err))
1318 self.current_test_case_info.logger.debug(
1319 "formatted exception is:\n%s" %
1320 "".join(format_exception(*err)))
1321
1322 def add_error(self, test, err, unittest_fn, error_type):
1323 if error_type == FAIL:
1324 self.log_error(test, err, 'addFailure')
1325 error_type_str = colorize("FAIL", RED)
1326 elif error_type == ERROR:
1327 self.log_error(test, err, 'addError')
1328 error_type_str = colorize("ERROR", RED)
1329 else:
1330 raise Exception('Error type %s cannot be used to record an '
1331 'error or a failure' % error_type)
1332
1333 unittest_fn(self, test, err)
1334 if self.current_test_case_info:
1335 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1336 (error_type_str,
1337 self.current_test_case_info.tempdir)
1338 self.symlink_failed()
1339 self.failed_test_cases_info.add(self.current_test_case_info)
1340 if is_core_present(self.current_test_case_info.tempdir):
1341 if not self.current_test_case_info.core_crash_test:
1342 if isinstance(test, unittest.suite._ErrorHolder):
1343 test_name = str(test)
1344 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001345 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001346 get_testcase_doc_name(test), test.id())
1347 self.current_test_case_info.core_crash_test = test_name
1348 self.core_crash_test_cases_info.add(
1349 self.current_test_case_info)
1350 else:
1351 self.result_string = '%s [no temp dir]' % error_type_str
1352
1353 self.send_result_through_pipe(test, error_type)
1354
Damjan Marionf56b77a2016-10-03 19:44:57 +02001355 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001356 """
1357 Record a test failed result
1358
1359 :param test:
1360 :param err: error message
1361
1362 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001363 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001364
Damjan Marionf56b77a2016-10-03 19:44:57 +02001365 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001366 """
1367 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001368
Klement Sekeraf62ae122016-10-11 11:47:09 +02001369 :param test:
1370 :param err: error message
1371
1372 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001373 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001374
Damjan Marionf56b77a2016-10-03 19:44:57 +02001375 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001376 """
1377 Get test description
1378
1379 :param test:
1380 :returns: test description
1381
1382 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001383 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001384
Damjan Marionf56b77a2016-10-03 19:44:57 +02001385 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001386 """
1387 Start a test
1388
1389 :param test:
1390
1391 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001392
1393 def print_header(test):
1394 if not hasattr(test.__class__, '_header_printed'):
1395 print(double_line_delim)
1396 print(colorize(getdoc(test).splitlines()[0], GREEN))
1397 print(double_line_delim)
1398 test.__class__._header_printed = True
1399
1400 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001401 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001402 unittest.TestResult.startTest(self, test)
1403 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001404 self.stream.writeln(
1405 "Starting " + self.getDescription(test) + " ...")
1406 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001407
Damjan Marionf56b77a2016-10-03 19:44:57 +02001408 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001409 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001410 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001411
1412 :param test:
1413
1414 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001415 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001416
Damjan Marionf56b77a2016-10-03 19:44:57 +02001417 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001418 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001419 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001420 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001421 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001422 else:
Ole Troan0c629322019-11-28 14:48:44 +01001423 self.stream.writeln("%-68s %4.2f %s" %
1424 (self.getDescription(test),
1425 time.time() - self.start_test,
1426 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001427
1428 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001429
Damjan Marionf56b77a2016-10-03 19:44:57 +02001430 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001431 """
1432 Print errors from running the test case
1433 """
juraj.linkesabec0122018-11-16 17:28:56 +01001434 if len(self.errors) > 0 or len(self.failures) > 0:
1435 self.stream.writeln()
1436 self.printErrorList('ERROR', self.errors)
1437 self.printErrorList('FAIL', self.failures)
1438
1439 # ^^ that is the last output from unittest before summary
1440 if not self.runner.print_summary:
1441 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1442 self.stream = devnull
1443 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001444
Damjan Marionf56b77a2016-10-03 19:44:57 +02001445 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001446 """
1447 Print error list to the output stream together with error type
1448 and test case description.
1449
1450 :param flavour: error type
1451 :param errors: iterable errors
1452
1453 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001454 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001455 self.stream.writeln(double_line_delim)
1456 self.stream.writeln("%s: %s" %
1457 (flavour, self.getDescription(test)))
1458 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001459 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001460
1461
Damjan Marionf56b77a2016-10-03 19:44:57 +02001462class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001463 """
Klement Sekera104543f2017-02-03 07:29:43 +01001464 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001465 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001466
Klement Sekeraf62ae122016-10-11 11:47:09 +02001467 @property
1468 def resultclass(self):
1469 """Class maintaining the results of the tests"""
1470 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001471
juraj.linkes184870a2018-07-16 14:22:01 +02001472 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001473 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001474 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001475 # ignore stream setting here, use hard-coded stdout to be in sync
1476 # with prints from VppTestCase methods ...
1477 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1478 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001479 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001480 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001481
juraj.linkesabec0122018-11-16 17:28:56 +01001482 self.orig_stream = self.stream
1483 self.resultclass.test_framework_result_pipe = result_pipe
1484
1485 self.print_summary = print_summary
1486
1487 def _makeResult(self):
1488 return self.resultclass(self.stream,
1489 self.descriptions,
1490 self.verbosity,
1491 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001492
Damjan Marionf56b77a2016-10-03 19:44:57 +02001493 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001494 """
1495 Run the tests
1496
1497 :param test:
1498
1499 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001500 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001501
1502 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001503 if not self.print_summary:
1504 self.stream = self.orig_stream
1505 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001506 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001507
1508
1509class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001510 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1511 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001512 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001513 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001514 if hasattr(self, 'testcase') and self.testcase.debug_all:
1515 if self.testcase.debug_gdbserver:
1516 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1517 .format(port=self.testcase.gdbserver_port)] + args
1518 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1519 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001520 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001521 self.app_name = os.path.basename(self.app_bin)
1522 if hasattr(self, 'role'):
1523 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001524 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001525 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001526 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001527 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001528
Dave Wallace24564332019-10-21 02:53:14 +00001529 def wait_for_enter(self):
1530 if not hasattr(self, 'testcase'):
1531 return
1532 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1533 print()
1534 print(double_line_delim)
1535 print("Spawned GDB Server for '{app}' with PID: {pid}"
1536 .format(app=self.app_name, pid=self.process.pid))
1537 elif self.testcase.debug_all and self.testcase.debug_gdb:
1538 print()
1539 print(double_line_delim)
1540 print("Spawned '{app}' with PID: {pid}"
1541 .format(app=self.app_name, pid=self.process.pid))
1542 else:
1543 return
1544 print(single_line_delim)
1545 print("You can debug '{app}' using:".format(app=self.app_name))
1546 if self.testcase.debug_gdbserver:
1547 print("sudo gdb " + self.app_bin +
1548 " -ex 'target remote localhost:{port}'"
1549 .format(port=self.testcase.gdbserver_port))
1550 print("Now is the time to attach gdb by running the above "
1551 "command, set up breakpoints etc., then resume from "
1552 "within gdb by issuing the 'continue' command")
1553 self.testcase.gdbserver_port += 1
1554 elif self.testcase.debug_gdb:
1555 print("sudo gdb " + self.app_bin +
1556 " -ex 'attach {pid}'".format(pid=self.process.pid))
1557 print("Now is the time to attach gdb by running the above "
1558 "command and set up breakpoints etc., then resume from"
1559 " within gdb by issuing the 'continue' command")
1560 print(single_line_delim)
1561 input("Press ENTER to continue running the testcase...")
1562
Neale Ranns812ed392017-10-16 04:20:13 -07001563 def run(self):
1564 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001565 if not os.path.exists(executable) or not os.access(
1566 executable, os.F_OK | os.X_OK):
1567 # Exit code that means some system file did not exist,
1568 # could not be opened, or had some other kind of error.
1569 self.result = os.EX_OSFILE
1570 raise EnvironmentError(
1571 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001572 self.logger.debug("Running executable: '{app}'"
1573 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001574 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001575 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001576 env["CK_LOG_FILE_NAME"] = "-"
1577 self.process = subprocess.Popen(
1578 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1579 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001580 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001581 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001582 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001583 self.logger.info("Return code is `%s'" % self.process.returncode)
1584 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001585 self.logger.info("Executable `{app}' wrote to stdout:"
1586 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001587 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001588 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001589 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001590 self.logger.info("Executable `{app}' wrote to stderr:"
1591 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001592 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001593 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001594 self.logger.info(single_line_delim)
1595 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001596
Klement Sekera6aa58b72019-05-16 14:34:55 +02001597
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001598if __name__ == '__main__':
1599 pass