blob: ba4576518b970b289ae5c864254c45a8154959a5 [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
220def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100221 os_id = os.getenv("OS_ID", "")
222 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200223
Klement Sekera6aa58b72019-05-16 14:34:55 +0200224
Klement Sekera3a350702019-09-02 14:26:26 +0000225running_on_centos = _running_on_centos()
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800226
Klement Sekerad3e671e2017-09-29 12:36:37 +0200227
Klement Sekera909a6a12017-08-08 04:33:53 +0200228class KeepAliveReporter(object):
229 """
230 Singleton object which reports test start to parent process
231 """
232 _shared_state = {}
233
234 def __init__(self):
235 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800236 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200237
238 @property
239 def pipe(self):
240 return self._pipe
241
242 @pipe.setter
243 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800244 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200245 raise Exception("Internal error - pipe should only be set once.")
246 self._pipe = pipe
247
juraj.linkes40dd73b2018-09-21 13:55:16 +0200248 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200249 """
250 Write current test tmpdir & desc to keep-alive pipe to signal liveness
251 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200252 if self.pipe is None:
253 # if not running forked..
254 return
255
Klement Sekera909a6a12017-08-08 04:33:53 +0200256 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200257 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200258 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200259 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200260
Dave Wallacee2efd122017-09-30 22:04:21 -0400261 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200262
263
Damjan Marionf56b77a2016-10-03 19:44:57 +0200264class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100265 """This subclass is a base class for VPP test cases that are implemented as
266 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200267 """
268
Ole Troana45dc072018-12-21 16:04:22 +0100269 extra_vpp_punt_config = []
270 extra_vpp_plugin_config = []
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400271 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100272
Klement Sekeraf62ae122016-10-11 11:47:09 +0200273 @property
274 def packet_infos(self):
275 """List of packet infos"""
276 return self._packet_infos
277
Klement Sekeradab231a2016-12-21 08:50:14 +0100278 @classmethod
279 def get_packet_count_for_if_idx(cls, dst_if_index):
280 """Get the number of packet info for specified destination if index"""
281 if dst_if_index in cls._packet_count_for_dst_if_idx:
282 return cls._packet_count_for_dst_if_idx[dst_if_index]
283 else:
284 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200285
286 @classmethod
287 def instance(cls):
288 """Return the instance of this testcase"""
289 return cls.test_instance
290
Damjan Marionf56b77a2016-10-03 19:44:57 +0200291 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200292 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000293 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200294 cls.debug_core = False
295 cls.debug_gdb = False
296 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000297 cls.debug_all = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200298 if d is None:
299 return
300 dl = d.lower()
301 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200302 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000303 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200304 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000305 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200306 cls.debug_gdbserver = True
307 else:
308 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000309 if dl == "gdb-all" or dl == "gdbserver-all":
310 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200311
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800312 @staticmethod
313 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200314 cpu_usage_list = [set(range(psutil.cpu_count()))]
315 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
316 if 'vpp_main' == p.info['name']]
317 for vpp_process in vpp_processes:
318 for cpu_usage_set in cpu_usage_list:
319 try:
320 cpu_num = vpp_process.cpu_num()
321 if cpu_num in cpu_usage_set:
322 cpu_usage_set_index = cpu_usage_list.index(
323 cpu_usage_set)
324 if cpu_usage_set_index == len(cpu_usage_list) - 1:
325 cpu_usage_list.append({cpu_num})
326 else:
327 cpu_usage_list[cpu_usage_set_index + 1].add(
328 cpu_num)
329 cpu_usage_set.remove(cpu_num)
330 break
331 except psutil.NoSuchProcess:
332 pass
333
334 for cpu_usage_set in cpu_usage_list:
335 if len(cpu_usage_set) > 0:
336 min_usage_set = cpu_usage_set
337 break
338
339 return random.choice(tuple(min_usage_set))
340
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800341 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200342 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200343 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400344 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100345 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400346 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100347 c = os.getenv("CACHE_OUTPUT", "1")
348 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200349 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100350 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
351 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400352 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100353 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
354 plugin_path = None
355 if cls.plugin_path is not None:
356 if cls.extern_plugin_path is not None:
357 plugin_path = "%s:%s" % (
358 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100359 else:
360 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100361 elif cls.extern_plugin_path is not None:
362 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100363 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100364 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100365 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100366 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100367 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100368 if size is not None:
369 coredump_size = "coredump-size %s" % size
370 if coredump_size is None:
371 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200372
Ole Troana45dc072018-12-21 16:04:22 +0100373 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000374 if not hasattr(cls, "worker_config"):
375 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200376
Ole Troana45dc072018-12-21 16:04:22 +0100377 cls.vpp_cmdline = [cls.vpp_bin, "unix",
378 "{", "nodaemon", debug_cli, "full-coredump",
379 coredump_size, "runtime-dir", cls.tempdir, "}",
380 "api-trace", "{", "on", "}", "api-segment", "{",
381 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000382 "main-core", str(cpu_core_number),
383 cls.worker_config, "}",
Dave Barach4ed25982019-12-25 09:24:58 -0500384 "physmem", "{", "max-size", "32m", "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200385 "statseg", "{", "socket-name", cls.stats_sock, "}",
386 "socksvr", "{", "socket-name", cls.api_sock, "}",
387 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100388 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200389 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100390 "}", "plugin", "unittest_plugin.so", "{", "enable",
391 "}"] + cls.extra_vpp_plugin_config + ["}", ]
392 if cls.extra_vpp_punt_config is not None:
393 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100394 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100395 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400396 if cls.test_plugin_path is not None:
397 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
398
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100399 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
400 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200401
402 @classmethod
403 def wait_for_enter(cls):
404 if cls.debug_gdbserver:
405 print(double_line_delim)
406 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
407 elif cls.debug_gdb:
408 print(double_line_delim)
409 print("Spawned VPP with PID: %d" % cls.vpp.pid)
410 else:
411 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
412 return
413 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000414 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200415 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400416 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000417 " -ex 'target remote localhost:{port}'"
418 .format(port=cls.gdbserver_port))
419 print("Now is the time to attach gdb by running the above "
420 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200421 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000422 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200423 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400424 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000425 print("Now is the time to attach gdb by running the above "
426 "command and set up breakpoints etc., then resume VPP from"
427 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200428 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800429 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200430
431 @classmethod
432 def run_vpp(cls):
433 cmdline = cls.vpp_cmdline
434
435 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100436 gdbserver = '/usr/bin/gdbserver'
437 if not os.path.isfile(gdbserver) or \
438 not os.access(gdbserver, os.X_OK):
439 raise Exception("gdbserver binary '%s' does not exist or is "
440 "not executable" % gdbserver)
441
Dave Wallace24564332019-10-21 02:53:14 +0000442 cmdline = [gdbserver, 'localhost:{port}'
443 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200444 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
445
Klement Sekera931be3a2016-11-03 05:36:01 +0100446 try:
447 cls.vpp = subprocess.Popen(cmdline,
448 stdout=subprocess.PIPE,
449 stderr=subprocess.PIPE,
450 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800451 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800452 cls.logger.critical("Subprocess returned with non-0 return code: ("
453 "%s)", e.returncode)
454 raise
455 except OSError as e:
456 cls.logger.critical("Subprocess returned with OS error: "
457 "(%s) %s", e.errno, e.strerror)
458 raise
459 except Exception as e:
460 cls.logger.exception("Subprocess returned unexpected from "
461 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100462 raise
463
Klement Sekera277b89c2016-10-28 13:20:27 +0200464 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100465
Damjan Marionf56b77a2016-10-03 19:44:57 +0200466 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000467 def wait_for_coredump(cls):
468 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400469 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000470 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400471 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000472 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400473 ok = False
474 while time.time() < deadline:
475 cls.sleep(1)
476 size = curr_size
477 curr_size = os.path.getsize(corefile)
478 if size == curr_size:
479 ok = True
480 break
481 if not ok:
482 cls.logger.error("Timed out waiting for coredump to complete:"
483 " %s", corefile)
484 else:
485 cls.logger.error("Coredump complete: %s, size %d",
486 corefile, curr_size)
487
488 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200489 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200490 """
491 Perform class setup before running the testcase
492 Remove shared memory files, start vpp and connect the vpp-api
493 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800494 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100495 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100496 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000497 seed = os.environ["RND_SEED"]
498 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100499 if hasattr(cls, 'parallel_handler'):
500 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100501 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700502
Klement Sekeraf62ae122016-10-11 11:47:09 +0200503 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200504 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200505 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200506 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200507 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
508 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100509 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
510 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200511 cls.file_handler.setLevel(DEBUG)
512 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700513 cls.logger.debug("--- setUpClass() for %s called ---" %
514 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200515 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200516 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200517 cls.logger.info("Temporary dir is %s, shm prefix is %s",
518 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000519 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200520 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100521 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100522 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200523 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100524 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100525 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200526 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200527 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200528 # need to catch exceptions here because if we raise, then the cleanup
529 # doesn't get called and we might end with a zombie vpp
530 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200531 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200532 cls.reporter.send_keep_alive(cls, 'setUpClass')
533 VppTestResult.current_test_case_info = TestCaseInfo(
534 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100535 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100536 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100537 cls.pump_thread_stop_flag = Event()
538 cls.pump_thread_wakeup_pipe = os.pipe()
539 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100540 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100541 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200542 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400543 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200544 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400545 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100546 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400547 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100548 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400549 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100550 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200551 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200552 try:
553 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100554 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200555 cls.vpp_startup_failed = True
556 cls.logger.critical(
557 "VPP died shortly after startup, check the"
558 " output to standard error for possible cause")
559 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100560 try:
561 cls.vapi.connect()
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500562 except vpp_papi.VPPIOError as e:
563 cls.logger.debug("Exception connecting to vapi: %s" % e)
564 cls.vapi.disconnect()
565
Klement Sekera085f5c02016-11-24 01:59:16 +0100566 if cls.debug_gdbserver:
567 print(colorize("You're running VPP inside gdbserver but "
568 "VPP-API connection failed, did you forget "
569 "to 'continue' VPP from within gdb?", RED))
570 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000571 except Exception as e:
572 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400573
574 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100575 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200576
Damjan Marionf56b77a2016-10-03 19:44:57 +0200577 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500578 def _debug_quit(cls):
579 if (cls.debug_gdbserver or cls.debug_gdb):
580 try:
581 cls.vpp.poll()
582
583 if cls.vpp.returncode is None:
584 print()
585 print(double_line_delim)
586 print("VPP or GDB server is still running")
587 print(single_line_delim)
588 input("When done debugging, press ENTER to kill the "
589 "process and finish running the testcase...")
590 except AttributeError:
591 pass
592
593 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200594 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200595 """
596 Disconnect vpp-api, kill vpp and cleanup shared memory files
597 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500598 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200599
juraj.linkes184870a2018-07-16 14:22:01 +0200600 # first signal that we want to stop the pump thread, then wake it up
601 if hasattr(cls, 'pump_thread_stop_flag'):
602 cls.pump_thread_stop_flag.set()
603 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100604 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100605 if hasattr(cls, 'pump_thread'):
606 cls.logger.debug("Waiting for pump thread to stop")
607 cls.pump_thread.join()
608 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500609 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100610 cls.vpp_stderr_reader_thread.join()
611
Klement Sekeraf62ae122016-10-11 11:47:09 +0200612 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100613 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100614 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700615 cls.logger.debug("Disconnecting class vapi client on %s",
616 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100617 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700618 cls.logger.debug("Deleting class vapi attribute on %s",
619 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100620 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200621 cls.vpp.poll()
622 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000623 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100624 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400625 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100626 cls.logger.debug("Waiting for vpp to die")
627 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700628 cls.logger.debug("Deleting class vpp attribute on %s",
629 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200630 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200631
Klement Sekera3747c752017-04-10 06:30:17 +0200632 if cls.vpp_startup_failed:
633 stdout_log = cls.logger.info
634 stderr_log = cls.logger.critical
635 else:
636 stdout_log = cls.logger.info
637 stderr_log = cls.logger.info
638
Klement Sekerae4504c62016-12-08 10:16:41 +0100639 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200640 stdout_log(single_line_delim)
641 stdout_log('VPP output to stdout while running %s:', cls.__name__)
642 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100643 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200644 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
645 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200646 stdout_log('\n%s', vpp_output)
647 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200648
Klement Sekerae4504c62016-12-08 10:16:41 +0100649 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200650 stderr_log(single_line_delim)
651 stderr_log('VPP output to stderr while running %s:', cls.__name__)
652 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100653 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200654 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
655 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200656 stderr_log('\n%s', vpp_output)
657 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200658
Damjan Marionf56b77a2016-10-03 19:44:57 +0200659 @classmethod
660 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200661 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700662 cls.logger.debug("--- tearDownClass() for %s called ---" %
663 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200664 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200665 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200666 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100667 cls.reset_packet_infos()
668 if debug_framework:
669 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200670
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700671 def show_commands_at_teardown(self):
672 """ Allow subclass specific teardown logging additions."""
673 self.logger.info("--- No test specific show commands provided. ---")
674
Damjan Marionf56b77a2016-10-03 19:44:57 +0200675 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200676 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100677 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
678 (self.__class__.__name__, self._testMethodName,
679 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700680
681 try:
682 if not self.vpp_dead:
683 self.logger.debug(self.vapi.cli("show trace max 1000"))
684 self.logger.info(self.vapi.ppcli("show interface"))
685 self.logger.info(self.vapi.ppcli("show hardware"))
686 self.logger.info(self.statistics.set_errors_str())
687 self.logger.info(self.vapi.ppcli("show run"))
688 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400689 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700690 self.logger.info("Logging testcase specific show commands.")
691 self.show_commands_at_teardown()
692 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500693 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000694 m = self._testMethodName
695 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500696 tmp_api_trace = "/tmp/%s" % api_trace
697 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
698 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
699 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
700 vpp_api_trace_log))
701 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500702 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500703 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700704 except VppTransportShmemIOError:
705 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
706 "Cannot log show commands.")
707 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100708 else:
709 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200710
Damjan Marionf56b77a2016-10-03 19:44:57 +0200711 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200712 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800713 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200714 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100715 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400716
717 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
718 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100719 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100720 self.vpp_stdout_deque.append(
721 "--- test setUp() for %s.%s(%s) starts here ---\n" %
722 (self.__class__.__name__, self._testMethodName,
723 self._testMethodDoc))
724 self.vpp_stderr_deque.append(
725 "--- test setUp() for %s.%s(%s) starts here ---\n" %
726 (self.__class__.__name__, self._testMethodName,
727 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200728 self.vapi.cli("clear trace")
729 # store the test instance inside the test class - so that objects
730 # holding the class can access instance methods (like assertEqual)
731 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200732
Damjan Marionf56b77a2016-10-03 19:44:57 +0200733 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200734 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200735 """
736 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200737
Klement Sekera75e7d132017-09-20 08:26:30 +0200738 :param interfaces: iterable interface indexes (if None,
739 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200740
Klement Sekeraf62ae122016-10-11 11:47:09 +0200741 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200742 if interfaces is None:
743 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200744 for i in interfaces:
745 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200746
Damjan Marionf56b77a2016-10-03 19:44:57 +0200747 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100748 def register_capture(cls, cap_name):
749 """ Register a capture in the testclass """
750 # add to the list of captures with current timestamp
751 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100752
753 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000754 def get_vpp_time(cls):
755 return float(cls.vapi.cli('show clock').replace("Time now ", ""))
756
757 @classmethod
758 def sleep_on_vpp_time(cls, sec):
759 """ Sleep according to time in VPP world """
760 # On a busy system with many processes
761 # we might end up with VPP time being slower than real world
762 # So take that into account when waiting for VPP to do something
763 start_time = cls.get_vpp_time()
764 while cls.get_vpp_time() - start_time < sec:
765 cls.sleep(0.1)
766
767 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100768 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000769 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100770 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200771 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000772 # PG, when starts, runs to completion -
773 # so let's avoid a race condition,
774 # and wait a little till it's done.
775 # Then clean it up - and then be gone.
776 deadline = time.time() + 300
777 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
778 cls.sleep(0.01) # yield
779 if time.time() > deadline:
780 cls.logger.error("Timeout waiting for pg to stop")
781 break
782 for stamp, cap_name in cls._captures:
783 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100784 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200785
Damjan Marionf56b77a2016-10-03 19:44:57 +0200786 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200787 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200788 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100789 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200790
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100791 :param interfaces: iterable indexes of the interfaces.
792 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200793
Klement Sekeraf62ae122016-10-11 11:47:09 +0200794 """
795 result = []
796 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200797 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200798 setattr(cls, intf.name, intf)
799 result.append(intf)
800 cls.pg_interfaces = result
801 return result
802
Matej Klotton0178d522016-11-04 11:11:44 +0100803 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200804 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100805 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100806 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100807
Klement Sekerab9ef2732018-06-24 22:49:33 +0200808 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100809 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100810 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200811 result = [VppLoInterface(cls) for i in range(count)]
812 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100813 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100814 cls.lo_interfaces = result
815 return result
816
Neale Ranns192b13f2019-03-15 02:16:20 -0700817 @classmethod
818 def create_bvi_interfaces(cls, count):
819 """
820 Create BVI interfaces.
821
822 :param count: number of interfaces created.
823 :returns: List of created interfaces.
824 """
825 result = [VppBviInterface(cls) for i in range(count)]
826 for intf in result:
827 setattr(cls, intf.name, intf)
828 cls.bvi_interfaces = result
829 return result
830
Damjan Marionf56b77a2016-10-03 19:44:57 +0200831 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200832 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200833 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200834 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200835 NOTE: Currently works only when Raw layer is present.
836
837 :param packet: packet
838 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200839 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200840
841 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200842 packet_len = len(packet) + 4
843 extend = size - packet_len
844 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200845 num = (extend // len(padding)) + 1
846 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200847
Klement Sekeradab231a2016-12-21 08:50:14 +0100848 @classmethod
849 def reset_packet_infos(cls):
850 """ Reset the list of packet info objects and packet counts to zero """
851 cls._packet_infos = {}
852 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200853
Klement Sekeradab231a2016-12-21 08:50:14 +0100854 @classmethod
855 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200856 """
857 Create packet info object containing the source and destination indexes
858 and add it to the testcase's packet info list
859
Klement Sekeradab231a2016-12-21 08:50:14 +0100860 :param VppInterface src_if: source interface
861 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200862
863 :returns: _PacketInfo object
864
865 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200866 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100867 info.index = len(cls._packet_infos)
868 info.src = src_if.sw_if_index
869 info.dst = dst_if.sw_if_index
870 if isinstance(dst_if, VppSubInterface):
871 dst_idx = dst_if.parent.sw_if_index
872 else:
873 dst_idx = dst_if.sw_if_index
874 if dst_idx in cls._packet_count_for_dst_if_idx:
875 cls._packet_count_for_dst_if_idx[dst_idx] += 1
876 else:
877 cls._packet_count_for_dst_if_idx[dst_idx] = 1
878 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200879 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200880
Damjan Marionf56b77a2016-10-03 19:44:57 +0200881 @staticmethod
882 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200883 """
884 Convert _PacketInfo object to packet payload
885
886 :param info: _PacketInfo object
887
888 :returns: string containing serialized data from packet info
889 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100890 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
891 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200892
Damjan Marionf56b77a2016-10-03 19:44:57 +0200893 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800894 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200895 """
896 Convert packet payload to _PacketInfo object
897
898 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700899 :type payload: <class 'scapy.packet.Raw'>
900 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800901 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700902 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200903 :returns: _PacketInfo object containing de-serialized data from payload
904
905 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800906 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200907 info = _PacketInfo()
908 info.index = int(numbers[0])
909 info.src = int(numbers[1])
910 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100911 info.ip = int(numbers[3])
912 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200913 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200914
Damjan Marionf56b77a2016-10-03 19:44:57 +0200915 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200916 """
917 Iterate over the packet info list stored in the testcase
918 Start iteration with first element if info is None
919 Continue based on index in info if info is specified
920
921 :param info: info or None
922 :returns: next info in list or None if no more infos
923 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200924 if info is None:
925 next_index = 0
926 else:
927 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100928 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200929 return None
930 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100931 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200932
Klement Sekeraf62ae122016-10-11 11:47:09 +0200933 def get_next_packet_info_for_interface(self, src_index, info):
934 """
935 Search the packet info list for the next packet info with same source
936 interface index
937
938 :param src_index: source interface index to search for
939 :param info: packet info - where to start the search
940 :returns: packet info or None
941
942 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200943 while True:
944 info = self.get_next_packet_info(info)
945 if info is None:
946 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200947 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200948 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200949
Klement Sekeraf62ae122016-10-11 11:47:09 +0200950 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
951 """
952 Search the packet info list for the next packet info with same source
953 and destination interface indexes
954
955 :param src_index: source interface index to search for
956 :param dst_index: destination interface index to search for
957 :param info: packet info - where to start the search
958 :returns: packet info or None
959
960 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200961 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200962 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200963 if info is None:
964 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200965 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200966 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200967
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200968 def assert_equal(self, real_value, expected_value, name_or_class=None):
969 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100970 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200971 return
972 try:
973 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
974 msg = msg % (getdoc(name_or_class).strip(),
975 real_value, str(name_or_class(real_value)),
976 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100977 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200978 msg = "Invalid %s: %s does not match expected value %s" % (
979 name_or_class, real_value, expected_value)
980
981 self.assertEqual(real_value, expected_value, msg)
982
Klement Sekerab17dd962017-01-09 07:43:48 +0100983 def assert_in_range(self,
984 real_value,
985 expected_min,
986 expected_max,
987 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200988 if name is None:
989 msg = None
990 else:
991 msg = "Invalid %s: %s out of range <%s,%s>" % (
992 name, real_value, expected_min, expected_max)
993 self.assertTrue(expected_min <= real_value <= expected_max, msg)
994
Klement Sekerad81ae412018-05-16 10:52:54 +0200995 def assert_packet_checksums_valid(self, packet,
996 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700997 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200998 udp_layers = ['UDP', 'UDPerror']
999 checksum_fields = ['cksum', 'chksum']
1000 checksums = []
1001 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001002 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001003 while True:
1004 layer = temp.getlayer(counter)
1005 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001006 layer = layer.copy()
1007 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001008 for cf in checksum_fields:
1009 if hasattr(layer, cf):
1010 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001011 0 == getattr(layer, cf) and \
1012 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001013 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001014 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001015 checksums.append((counter, cf))
1016 else:
1017 break
1018 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001019 if 0 == len(checksums):
1020 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001021 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001022 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001023 calc_sum = getattr(temp[layer], cf)
1024 self.assert_equal(
1025 getattr(received[layer], cf), calc_sum,
1026 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1027 self.logger.debug(
1028 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1029 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001030
1031 def assert_checksum_valid(self, received_packet, layer,
1032 field_name='chksum',
1033 ignore_zero_checksum=False):
1034 """ Check checksum of received packet on given layer """
1035 received_packet_checksum = getattr(received_packet[layer], field_name)
1036 if ignore_zero_checksum and 0 == received_packet_checksum:
1037 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001038 recalculated = received_packet.__class__(
1039 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001040 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001041 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001042 self.assert_equal(received_packet_checksum,
1043 getattr(recalculated[layer], field_name),
1044 "packet checksum on layer: %s" % layer)
1045
1046 def assert_ip_checksum_valid(self, received_packet,
1047 ignore_zero_checksum=False):
1048 self.assert_checksum_valid(received_packet, 'IP',
1049 ignore_zero_checksum=ignore_zero_checksum)
1050
1051 def assert_tcp_checksum_valid(self, received_packet,
1052 ignore_zero_checksum=False):
1053 self.assert_checksum_valid(received_packet, 'TCP',
1054 ignore_zero_checksum=ignore_zero_checksum)
1055
1056 def assert_udp_checksum_valid(self, received_packet,
1057 ignore_zero_checksum=True):
1058 self.assert_checksum_valid(received_packet, 'UDP',
1059 ignore_zero_checksum=ignore_zero_checksum)
1060
1061 def assert_embedded_icmp_checksum_valid(self, received_packet):
1062 if received_packet.haslayer(IPerror):
1063 self.assert_checksum_valid(received_packet, 'IPerror')
1064 if received_packet.haslayer(TCPerror):
1065 self.assert_checksum_valid(received_packet, 'TCPerror')
1066 if received_packet.haslayer(UDPerror):
1067 self.assert_checksum_valid(received_packet, 'UDPerror',
1068 ignore_zero_checksum=True)
1069 if received_packet.haslayer(ICMPerror):
1070 self.assert_checksum_valid(received_packet, 'ICMPerror')
1071
1072 def assert_icmp_checksum_valid(self, received_packet):
1073 self.assert_checksum_valid(received_packet, 'ICMP')
1074 self.assert_embedded_icmp_checksum_valid(received_packet)
1075
1076 def assert_icmpv6_checksum_valid(self, pkt):
1077 if pkt.haslayer(ICMPv6DestUnreach):
1078 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1079 self.assert_embedded_icmp_checksum_valid(pkt)
1080 if pkt.haslayer(ICMPv6EchoRequest):
1081 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1082 if pkt.haslayer(ICMPv6EchoReply):
1083 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1084
Klement Sekera3a343d42019-05-16 14:35:46 +02001085 def get_packet_counter(self, counter):
1086 if counter.startswith("/"):
1087 counter_value = self.statistics.get_counter(counter)
1088 else:
1089 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001090 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001091 for i in range(1, len(counters) - 1):
1092 results = counters[i].split()
1093 if results[1] == counter:
1094 counter_value = int(results[0])
1095 break
1096 return counter_value
1097
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001098 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001099 counter_value = self.get_packet_counter(counter)
1100 self.assert_equal(counter_value, expected_value,
1101 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001102
Ole Troan233e4682019-05-16 15:01:34 +02001103 def assert_error_counter_equal(self, counter, expected_value):
1104 counter_value = self.statistics.get_err_counter(counter)
1105 self.assert_equal(counter_value, expected_value,
1106 "error counter `%s'" % counter)
1107
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001108 @classmethod
1109 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001110
1111 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1112 # * by Guido, only the main thread can be interrupted.
1113 # */
1114 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1115 if timeout == 0:
1116 # yield quantum
1117 if hasattr(os, 'sched_yield'):
1118 os.sched_yield()
1119 else:
1120 time.sleep(0)
1121 return
1122
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001123 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001124 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001125 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001126 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001127 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001128 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001129 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001130 "slept for %es instead of ~%es!",
1131 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001132 if hasattr(cls, 'logger'):
1133 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001134 "Finished sleep (%s) - slept %es (wanted %es)",
1135 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001136
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001137 def pg_send(self, intf, pkts, worker=None):
Neale Ranns52fae862018-01-08 04:41:42 -08001138 self.vapi.cli("clear trace")
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001139 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001140 self.pg_enable_capture(self.pg_interfaces)
1141 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001142
1143 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1144 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001145 if not timeout:
1146 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001147 for i in self.pg_interfaces:
1148 i.get_capture(0, timeout=timeout)
1149 i.assert_nothing_captured(remark=remark)
1150 timeout = 0.1
1151
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001152 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None):
Neale Rannsd7603d92019-03-28 08:56:10 +00001153 if not n_rx:
1154 n_rx = len(pkts)
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001155 self.pg_send(intf, pkts, worker=worker)
Neale Rannsd7603d92019-03-28 08:56:10 +00001156 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001157 return rx
1158
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001159 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1160 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001161 rx = output.get_capture(len(pkts))
1162 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001163 if not timeout:
1164 timeout = 1
1165 for i in self.pg_interfaces:
1166 if i not in outputs:
1167 i.get_capture(0, timeout=timeout)
1168 i.assert_nothing_captured()
1169 timeout = 0.1
1170
Neale Ranns52fae862018-01-08 04:41:42 -08001171 return rx
1172
Damjan Marionf56b77a2016-10-03 19:44:57 +02001173
juraj.linkes184870a2018-07-16 14:22:01 +02001174def get_testcase_doc_name(test):
1175 return getdoc(test.__class__).splitlines()[0]
1176
1177
Ole Trøan5ba91592018-11-22 10:01:09 +00001178def get_test_description(descriptions, test):
1179 short_description = test.shortDescription()
1180 if descriptions and short_description:
1181 return short_description
1182 else:
1183 return str(test)
1184
1185
juraj.linkes40dd73b2018-09-21 13:55:16 +02001186class TestCaseInfo(object):
1187 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1188 self.logger = logger
1189 self.tempdir = tempdir
1190 self.vpp_pid = vpp_pid
1191 self.vpp_bin_path = vpp_bin_path
1192 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001193
1194
Damjan Marionf56b77a2016-10-03 19:44:57 +02001195class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001196 """
1197 @property result_string
1198 String variable to store the test case result string.
1199 @property errors
1200 List variable containing 2-tuples of TestCase instances and strings
1201 holding formatted tracebacks. Each tuple represents a test which
1202 raised an unexpected exception.
1203 @property failures
1204 List variable containing 2-tuples of TestCase instances and strings
1205 holding formatted tracebacks. Each tuple represents a test where
1206 a failure was explicitly signalled using the TestCase.assert*()
1207 methods.
1208 """
1209
juraj.linkes40dd73b2018-09-21 13:55:16 +02001210 failed_test_cases_info = set()
1211 core_crash_test_cases_info = set()
1212 current_test_case_info = None
1213
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001214 def __init__(self, stream=None, descriptions=None, verbosity=None,
1215 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001216 """
Klement Sekerada505f62017-01-04 12:58:53 +01001217 :param stream File descriptor to store where to report test results.
1218 Set to the standard error stream by default.
1219 :param descriptions Boolean variable to store information if to use
1220 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001221 :param verbosity Integer variable to store required verbosity level.
1222 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001223 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001224 self.stream = stream
1225 self.descriptions = descriptions
1226 self.verbosity = verbosity
1227 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001228 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001229
Damjan Marionf56b77a2016-10-03 19:44:57 +02001230 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001231 """
1232 Record a test succeeded result
1233
1234 :param test:
1235
1236 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001237 if self.current_test_case_info:
1238 self.current_test_case_info.logger.debug(
1239 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1240 test._testMethodName,
1241 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001242 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001243 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001244
juraj.linkescae64f82018-09-19 15:01:47 +02001245 self.send_result_through_pipe(test, PASS)
1246
Klement Sekeraf62ae122016-10-11 11:47:09 +02001247 def addSkip(self, test, reason):
1248 """
1249 Record a test skipped.
1250
1251 :param test:
1252 :param reason:
1253
1254 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001255 if self.current_test_case_info:
1256 self.current_test_case_info.logger.debug(
1257 "--- addSkip() %s.%s(%s) called, reason is %s" %
1258 (test.__class__.__name__, test._testMethodName,
1259 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001260 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001261 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001262
juraj.linkescae64f82018-09-19 15:01:47 +02001263 self.send_result_through_pipe(test, SKIP)
1264
juraj.linkes40dd73b2018-09-21 13:55:16 +02001265 def symlink_failed(self):
1266 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001267 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001268 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001269 link_path = os.path.join(
1270 failed_dir,
1271 '%s-FAILED' %
1272 os.path.basename(self.current_test_case_info.tempdir))
1273 if self.current_test_case_info.logger:
1274 self.current_test_case_info.logger.debug(
1275 "creating a link to the failed test")
1276 self.current_test_case_info.logger.debug(
1277 "os.symlink(%s, %s)" %
1278 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001279 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001280 if self.current_test_case_info.logger:
1281 self.current_test_case_info.logger.debug(
1282 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001283 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001284 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001285
Klement Sekeraf413bef2017-08-15 07:09:02 +02001286 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001287 if self.current_test_case_info.logger:
1288 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001289
juraj.linkescae64f82018-09-19 15:01:47 +02001290 def send_result_through_pipe(self, test, result):
1291 if hasattr(self, 'test_framework_result_pipe'):
1292 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001293 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001294 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001295
juraj.linkes40dd73b2018-09-21 13:55:16 +02001296 def log_error(self, test, err, fn_name):
1297 if self.current_test_case_info:
1298 if isinstance(test, unittest.suite._ErrorHolder):
1299 test_name = test.description
1300 else:
1301 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1302 test._testMethodName,
1303 test._testMethodDoc)
1304 self.current_test_case_info.logger.debug(
1305 "--- %s() %s called, err is %s" %
1306 (fn_name, test_name, err))
1307 self.current_test_case_info.logger.debug(
1308 "formatted exception is:\n%s" %
1309 "".join(format_exception(*err)))
1310
1311 def add_error(self, test, err, unittest_fn, error_type):
1312 if error_type == FAIL:
1313 self.log_error(test, err, 'addFailure')
1314 error_type_str = colorize("FAIL", RED)
1315 elif error_type == ERROR:
1316 self.log_error(test, err, 'addError')
1317 error_type_str = colorize("ERROR", RED)
1318 else:
1319 raise Exception('Error type %s cannot be used to record an '
1320 'error or a failure' % error_type)
1321
1322 unittest_fn(self, test, err)
1323 if self.current_test_case_info:
1324 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1325 (error_type_str,
1326 self.current_test_case_info.tempdir)
1327 self.symlink_failed()
1328 self.failed_test_cases_info.add(self.current_test_case_info)
1329 if is_core_present(self.current_test_case_info.tempdir):
1330 if not self.current_test_case_info.core_crash_test:
1331 if isinstance(test, unittest.suite._ErrorHolder):
1332 test_name = str(test)
1333 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001334 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001335 get_testcase_doc_name(test), test.id())
1336 self.current_test_case_info.core_crash_test = test_name
1337 self.core_crash_test_cases_info.add(
1338 self.current_test_case_info)
1339 else:
1340 self.result_string = '%s [no temp dir]' % error_type_str
1341
1342 self.send_result_through_pipe(test, error_type)
1343
Damjan Marionf56b77a2016-10-03 19:44:57 +02001344 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001345 """
1346 Record a test failed result
1347
1348 :param test:
1349 :param err: error message
1350
1351 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001352 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001353
Damjan Marionf56b77a2016-10-03 19:44:57 +02001354 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001355 """
1356 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001357
Klement Sekeraf62ae122016-10-11 11:47:09 +02001358 :param test:
1359 :param err: error message
1360
1361 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001362 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001363
Damjan Marionf56b77a2016-10-03 19:44:57 +02001364 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001365 """
1366 Get test description
1367
1368 :param test:
1369 :returns: test description
1370
1371 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001372 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001373
Damjan Marionf56b77a2016-10-03 19:44:57 +02001374 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001375 """
1376 Start a test
1377
1378 :param test:
1379
1380 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001381
1382 def print_header(test):
1383 if not hasattr(test.__class__, '_header_printed'):
1384 print(double_line_delim)
1385 print(colorize(getdoc(test).splitlines()[0], GREEN))
1386 print(double_line_delim)
1387 test.__class__._header_printed = True
1388
1389 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001390 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001391 unittest.TestResult.startTest(self, test)
1392 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001393 self.stream.writeln(
1394 "Starting " + self.getDescription(test) + " ...")
1395 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001396
Damjan Marionf56b77a2016-10-03 19:44:57 +02001397 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001398 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001399 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001400
1401 :param test:
1402
1403 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001404 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001405
Damjan Marionf56b77a2016-10-03 19:44:57 +02001406 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001407 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001408 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001409 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001410 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001411 else:
Ole Troan0c629322019-11-28 14:48:44 +01001412 self.stream.writeln("%-68s %4.2f %s" %
1413 (self.getDescription(test),
1414 time.time() - self.start_test,
1415 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001416
1417 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001418
Damjan Marionf56b77a2016-10-03 19:44:57 +02001419 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001420 """
1421 Print errors from running the test case
1422 """
juraj.linkesabec0122018-11-16 17:28:56 +01001423 if len(self.errors) > 0 or len(self.failures) > 0:
1424 self.stream.writeln()
1425 self.printErrorList('ERROR', self.errors)
1426 self.printErrorList('FAIL', self.failures)
1427
1428 # ^^ that is the last output from unittest before summary
1429 if not self.runner.print_summary:
1430 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1431 self.stream = devnull
1432 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001433
Damjan Marionf56b77a2016-10-03 19:44:57 +02001434 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001435 """
1436 Print error list to the output stream together with error type
1437 and test case description.
1438
1439 :param flavour: error type
1440 :param errors: iterable errors
1441
1442 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001443 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001444 self.stream.writeln(double_line_delim)
1445 self.stream.writeln("%s: %s" %
1446 (flavour, self.getDescription(test)))
1447 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001448 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001449
1450
Damjan Marionf56b77a2016-10-03 19:44:57 +02001451class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001452 """
Klement Sekera104543f2017-02-03 07:29:43 +01001453 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001454 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001455
Klement Sekeraf62ae122016-10-11 11:47:09 +02001456 @property
1457 def resultclass(self):
1458 """Class maintaining the results of the tests"""
1459 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001460
juraj.linkes184870a2018-07-16 14:22:01 +02001461 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001462 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001463 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001464 # ignore stream setting here, use hard-coded stdout to be in sync
1465 # with prints from VppTestCase methods ...
1466 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1467 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001468 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001469 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001470
juraj.linkesabec0122018-11-16 17:28:56 +01001471 self.orig_stream = self.stream
1472 self.resultclass.test_framework_result_pipe = result_pipe
1473
1474 self.print_summary = print_summary
1475
1476 def _makeResult(self):
1477 return self.resultclass(self.stream,
1478 self.descriptions,
1479 self.verbosity,
1480 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001481
Damjan Marionf56b77a2016-10-03 19:44:57 +02001482 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001483 """
1484 Run the tests
1485
1486 :param test:
1487
1488 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001489 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001490
1491 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001492 if not self.print_summary:
1493 self.stream = self.orig_stream
1494 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001495 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001496
1497
1498class Worker(Thread):
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001499 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1500 super(Worker, self).__init__(*args, **kwargs)
Neale Ranns812ed392017-10-16 04:20:13 -07001501 self.logger = logger
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001502 self.args = executable_args
Dave Wallace24564332019-10-21 02:53:14 +00001503 if hasattr(self, 'testcase') and self.testcase.debug_all:
1504 if self.testcase.debug_gdbserver:
1505 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1506 .format(port=self.testcase.gdbserver_port)] + args
1507 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1508 self.args.append(self.wait_for_gdb)
Paul Vinciguerra48bdbcd2019-12-04 19:43:53 -05001509 self.app_bin = executable_args[0]
Dave Wallace24564332019-10-21 02:53:14 +00001510 self.app_name = os.path.basename(self.app_bin)
1511 if hasattr(self, 'role'):
1512 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001513 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001514 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001515 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001516 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001517
Dave Wallace24564332019-10-21 02:53:14 +00001518 def wait_for_enter(self):
1519 if not hasattr(self, 'testcase'):
1520 return
1521 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1522 print()
1523 print(double_line_delim)
1524 print("Spawned GDB Server for '{app}' with PID: {pid}"
1525 .format(app=self.app_name, pid=self.process.pid))
1526 elif self.testcase.debug_all and self.testcase.debug_gdb:
1527 print()
1528 print(double_line_delim)
1529 print("Spawned '{app}' with PID: {pid}"
1530 .format(app=self.app_name, pid=self.process.pid))
1531 else:
1532 return
1533 print(single_line_delim)
1534 print("You can debug '{app}' using:".format(app=self.app_name))
1535 if self.testcase.debug_gdbserver:
1536 print("sudo gdb " + self.app_bin +
1537 " -ex 'target remote localhost:{port}'"
1538 .format(port=self.testcase.gdbserver_port))
1539 print("Now is the time to attach gdb by running the above "
1540 "command, set up breakpoints etc., then resume from "
1541 "within gdb by issuing the 'continue' command")
1542 self.testcase.gdbserver_port += 1
1543 elif self.testcase.debug_gdb:
1544 print("sudo gdb " + self.app_bin +
1545 " -ex 'attach {pid}'".format(pid=self.process.pid))
1546 print("Now is the time to attach gdb by running the above "
1547 "command and set up breakpoints etc., then resume from"
1548 " within gdb by issuing the 'continue' command")
1549 print(single_line_delim)
1550 input("Press ENTER to continue running the testcase...")
1551
Neale Ranns812ed392017-10-16 04:20:13 -07001552 def run(self):
1553 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001554 if not os.path.exists(executable) or not os.access(
1555 executable, os.F_OK | os.X_OK):
1556 # Exit code that means some system file did not exist,
1557 # could not be opened, or had some other kind of error.
1558 self.result = os.EX_OSFILE
1559 raise EnvironmentError(
1560 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001561 self.logger.debug("Running executable: '{app}'"
1562 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001563 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001564 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001565 env["CK_LOG_FILE_NAME"] = "-"
1566 self.process = subprocess.Popen(
1567 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1568 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001569 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001570 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001571 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001572 self.logger.info("Return code is `%s'" % self.process.returncode)
1573 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001574 self.logger.info("Executable `{app}' wrote to stdout:"
1575 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001576 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001577 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001578 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001579 self.logger.info("Executable `{app}' wrote to stderr:"
1580 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001581 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001582 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001583 self.logger.info(single_line_delim)
1584 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001585
Klement Sekera6aa58b72019-05-16 14:34:55 +02001586
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001587if __name__ == '__main__':
1588 pass