blob: 24ee8692976bb455b8a25dae0b484415a3c31a25 [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
31from vpp_papi.vpp_stats import VPPStats
Paul Vinciguerra499ea642019-03-15 09:39:19 -070032from vpp_papi.vpp_transport_shmem import VppTransportShmemIOError
Ole Trøan162989e2018-11-26 10:27:50 +000033from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
34 get_logger, colorize
35from vpp_object import VppObjectRegistry
36from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020037from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
38from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
39from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080040
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010041if os.name == 'posix' and sys.version_info[0] < 3:
42 # using subprocess32 is recommended by python official documentation
43 # @ https://docs.python.org/2/library/subprocess.html
44 import subprocess32 as subprocess
45else:
46 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020047
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080048# Python2/3 compatible
49try:
50 input = raw_input
51except NameError:
52 pass
53
juraj.linkescae64f82018-09-19 15:01:47 +020054PASS = 0
55FAIL = 1
56ERROR = 2
57SKIP = 3
58TEST_RUN = 4
59
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040060
61class BoolEnvironmentVariable(object):
62
63 def __init__(self, env_var_name, default='n', true_values=None):
64 self.name = env_var_name
65 self.default = default
66 self.true_values = true_values if true_values is not None else \
67 ("y", "yes", "1")
68
69 def __bool__(self):
70 return os.getenv(self.name, self.default).lower() in self.true_values
71
72 if sys.version_info[0] == 2:
73 __nonzero__ = __bool__
74
75 def __repr__(self):
76 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
77 (self.name, self.default, self.true_values)
78
79
80debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
81if debug_framework:
Klement Sekeraebbaf552018-02-17 13:41:33 +010082 import debug_internal
83
Klement Sekeraf62ae122016-10-11 11:47:09 +020084"""
85 Test framework module.
86
87 The module provides a set of tools for constructing and running tests and
88 representing the results.
89"""
90
Klement Sekeraf62ae122016-10-11 11:47:09 +020091
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040092class VppDiedError(Exception):
93 """ exception for reporting that the subprocess has died."""
94
95 signals_by_value = {v: k for k, v in signal.__dict__.items() if
96 k.startswith('SIG') and not k.startswith('SIG_')}
97
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040098 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040099 self.rv = rv
100 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400101 self.testcase = testcase
102 self.method_name = method_name
103
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400104 try:
105 self.signal_name = VppDiedError.signals_by_value[-rv]
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400106 except (KeyError, TypeError):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400107 pass
108
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400109 if testcase is None and method_name is None:
110 in_msg = ''
111 else:
112 in_msg = 'running %s.%s ' % (testcase, method_name)
113
114 msg = "VPP subprocess died %sunexpectedly with return code: %d%s." % (
115 in_msg,
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400116 self.rv,
Paul Vinciguerraf7457522019-07-13 09:35:38 -0400117 ' [%s]' % (self.signal_name if
118 self.signal_name is not None else ''))
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400119 super(VppDiedError, self).__init__(msg)
120
121
Damjan Marionf56b77a2016-10-03 19:44:57 +0200122class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200123 """Private class to create packet info object.
124
125 Help process information about the next packet.
126 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200127 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100128 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200129 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100130 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200131 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100132 #: Store the index of the destination packet generator interface
133 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200134 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100135 #: Store expected ip version
136 ip = -1
137 #: Store expected upper protocol
138 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100139 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200140 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200141
Matej Klotton16a14cd2016-12-07 15:09:13 +0100142 def __eq__(self, other):
143 index = self.index == other.index
144 src = self.src == other.src
145 dst = self.dst == other.dst
146 data = self.data == other.data
147 return index and src and dst and data
148
Klement Sekeraf62ae122016-10-11 11:47:09 +0200149
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100150def pump_output(testclass):
151 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100152 stdout_fragment = ""
153 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400154 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100155 readable = select.select([testclass.vpp.stdout.fileno(),
156 testclass.vpp.stderr.fileno(),
157 testclass.pump_thread_wakeup_pipe[0]],
158 [], [])[0]
159 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100160 read = os.read(testclass.vpp.stdout.fileno(), 102400)
161 if len(read) > 0:
Ole Troan45ec5702019-10-17 01:53:47 +0200162 split = read.decode('ascii',
163 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100164 if len(stdout_fragment) > 0:
165 split[0] = "%s%s" % (stdout_fragment, split[0])
166 if len(split) > 0 and split[-1].endswith("\n"):
167 limit = None
168 else:
169 limit = -1
170 stdout_fragment = split[-1]
171 testclass.vpp_stdout_deque.extend(split[:limit])
172 if not testclass.cache_vpp_output:
173 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100174 testclass.logger.info(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100175 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100176 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100177 read = os.read(testclass.vpp.stderr.fileno(), 102400)
178 if len(read) > 0:
Ole Troan6ed154f2019-10-15 19:31:55 +0200179 split = read.decode('ascii',
180 errors='backslashreplace').splitlines(True)
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100181 if len(stderr_fragment) > 0:
182 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan6ed154f2019-10-15 19:31:55 +0200183 if len(split) > 0 and split[-1].endswith("\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100184 limit = None
185 else:
186 limit = -1
187 stderr_fragment = split[-1]
Ole Troan6ed154f2019-10-15 19:31:55 +0200188
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100189 testclass.vpp_stderr_deque.extend(split[:limit])
190 if not testclass.cache_vpp_output:
191 for line in split[:limit]:
Benoît Ganne1a7ed5e2019-11-21 16:50:24 +0100192 testclass.logger.error(
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100193 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800194 # ignoring the dummy pipe here intentionally - the
195 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200196
197
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800198def _is_skip_aarch64_set():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400199 return BoolEnvironmentVariable('SKIP_AARCH64')
juraj.linkes68ebc832018-11-29 09:37:08 +0100200
Klement Sekera6aa58b72019-05-16 14:34:55 +0200201
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800202is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100203
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800204
205def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100206 return platform.machine() == 'aarch64'
207
Klement Sekera6aa58b72019-05-16 14:34:55 +0200208
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800209is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100210
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800211
212def _running_extended_tests():
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400213 return BoolEnvironmentVariable("EXTENDED_TESTS")
Klement Sekera87134932017-03-07 11:39:27 +0100214
Klement Sekera6aa58b72019-05-16 14:34:55 +0200215
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800216running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100217
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800218
219def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100220 os_id = os.getenv("OS_ID", "")
221 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200222
Klement Sekera6aa58b72019-05-16 14:34:55 +0200223
Klement Sekera3a350702019-09-02 14:26:26 +0000224running_on_centos = _running_on_centos()
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800225
Klement Sekerad3e671e2017-09-29 12:36:37 +0200226
Klement Sekera909a6a12017-08-08 04:33:53 +0200227class KeepAliveReporter(object):
228 """
229 Singleton object which reports test start to parent process
230 """
231 _shared_state = {}
232
233 def __init__(self):
234 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800235 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200236
237 @property
238 def pipe(self):
239 return self._pipe
240
241 @pipe.setter
242 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800243 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200244 raise Exception("Internal error - pipe should only be set once.")
245 self._pipe = pipe
246
juraj.linkes40dd73b2018-09-21 13:55:16 +0200247 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200248 """
249 Write current test tmpdir & desc to keep-alive pipe to signal liveness
250 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200251 if self.pipe is None:
252 # if not running forked..
253 return
254
Klement Sekera909a6a12017-08-08 04:33:53 +0200255 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200256 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200257 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200258 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200259
Dave Wallacee2efd122017-09-30 22:04:21 -0400260 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200261
262
Damjan Marionf56b77a2016-10-03 19:44:57 +0200263class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100264 """This subclass is a base class for VPP test cases that are implemented as
265 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200266 """
267
Ole Troana45dc072018-12-21 16:04:22 +0100268 extra_vpp_punt_config = []
269 extra_vpp_plugin_config = []
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400270 vapi_response_timeout = 5
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100271
Klement Sekeraf62ae122016-10-11 11:47:09 +0200272 @property
273 def packet_infos(self):
274 """List of packet infos"""
275 return self._packet_infos
276
Klement Sekeradab231a2016-12-21 08:50:14 +0100277 @classmethod
278 def get_packet_count_for_if_idx(cls, dst_if_index):
279 """Get the number of packet info for specified destination if index"""
280 if dst_if_index in cls._packet_count_for_dst_if_idx:
281 return cls._packet_count_for_dst_if_idx[dst_if_index]
282 else:
283 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200284
285 @classmethod
286 def instance(cls):
287 """Return the instance of this testcase"""
288 return cls.test_instance
289
Damjan Marionf56b77a2016-10-03 19:44:57 +0200290 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200291 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000292 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200293 cls.debug_core = False
294 cls.debug_gdb = False
295 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000296 cls.debug_all = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200297 if d is None:
298 return
299 dl = d.lower()
300 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200301 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000302 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200303 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000304 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200305 cls.debug_gdbserver = True
306 else:
307 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000308 if dl == "gdb-all" or dl == "gdbserver-all":
309 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200310
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800311 @staticmethod
312 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200313 cpu_usage_list = [set(range(psutil.cpu_count()))]
314 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
315 if 'vpp_main' == p.info['name']]
316 for vpp_process in vpp_processes:
317 for cpu_usage_set in cpu_usage_list:
318 try:
319 cpu_num = vpp_process.cpu_num()
320 if cpu_num in cpu_usage_set:
321 cpu_usage_set_index = cpu_usage_list.index(
322 cpu_usage_set)
323 if cpu_usage_set_index == len(cpu_usage_list) - 1:
324 cpu_usage_list.append({cpu_num})
325 else:
326 cpu_usage_list[cpu_usage_set_index + 1].add(
327 cpu_num)
328 cpu_usage_set.remove(cpu_num)
329 break
330 except psutil.NoSuchProcess:
331 pass
332
333 for cpu_usage_set in cpu_usage_list:
334 if len(cpu_usage_set) > 0:
335 min_usage_set = cpu_usage_set
336 break
337
338 return random.choice(tuple(min_usage_set))
339
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800340 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200341 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200342 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400343 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100344 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400345 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100346 c = os.getenv("CACHE_OUTPUT", "1")
347 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200348 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100349 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
350 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400351 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100352 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
353 plugin_path = None
354 if cls.plugin_path is not None:
355 if cls.extern_plugin_path is not None:
356 plugin_path = "%s:%s" % (
357 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100358 else:
359 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100360 elif cls.extern_plugin_path is not None:
361 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100362 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100363 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100364 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100365 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100366 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100367 if size is not None:
368 coredump_size = "coredump-size %s" % size
369 if coredump_size is None:
370 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200371
Ole Troana45dc072018-12-21 16:04:22 +0100372 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000373 if not hasattr(cls, "worker_config"):
374 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200375
Ole Troana45dc072018-12-21 16:04:22 +0100376 cls.vpp_cmdline = [cls.vpp_bin, "unix",
377 "{", "nodaemon", debug_cli, "full-coredump",
378 coredump_size, "runtime-dir", cls.tempdir, "}",
379 "api-trace", "{", "on", "}", "api-segment", "{",
380 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000381 "main-core", str(cpu_core_number),
382 cls.worker_config, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200383 "statseg", "{", "socket-name", cls.stats_sock, "}",
384 "socksvr", "{", "socket-name", cls.api_sock, "}",
385 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100386 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200387 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100388 "}", "plugin", "unittest_plugin.so", "{", "enable",
389 "}"] + cls.extra_vpp_plugin_config + ["}", ]
390 if cls.extra_vpp_punt_config is not None:
391 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100392 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100393 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400394 if cls.test_plugin_path is not None:
395 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
396
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100397 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
398 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200399
400 @classmethod
401 def wait_for_enter(cls):
402 if cls.debug_gdbserver:
403 print(double_line_delim)
404 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
405 elif cls.debug_gdb:
406 print(double_line_delim)
407 print("Spawned VPP with PID: %d" % cls.vpp.pid)
408 else:
409 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
410 return
411 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000412 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200413 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400414 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000415 " -ex 'target remote localhost:{port}'"
416 .format(port=cls.gdbserver_port))
417 print("Now is the time to attach gdb by running the above "
418 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200419 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000420 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200421 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400422 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000423 print("Now is the time to attach gdb by running the above "
424 "command and set up breakpoints etc., then resume VPP from"
425 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200426 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800427 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200428
429 @classmethod
430 def run_vpp(cls):
431 cmdline = cls.vpp_cmdline
432
433 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100434 gdbserver = '/usr/bin/gdbserver'
435 if not os.path.isfile(gdbserver) or \
436 not os.access(gdbserver, os.X_OK):
437 raise Exception("gdbserver binary '%s' does not exist or is "
438 "not executable" % gdbserver)
439
Dave Wallace24564332019-10-21 02:53:14 +0000440 cmdline = [gdbserver, 'localhost:{port}'
441 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200442 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
443
Klement Sekera931be3a2016-11-03 05:36:01 +0100444 try:
445 cls.vpp = subprocess.Popen(cmdline,
446 stdout=subprocess.PIPE,
447 stderr=subprocess.PIPE,
448 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800449 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800450 cls.logger.critical("Subprocess returned with non-0 return code: ("
451 "%s)", e.returncode)
452 raise
453 except OSError as e:
454 cls.logger.critical("Subprocess returned with OS error: "
455 "(%s) %s", e.errno, e.strerror)
456 raise
457 except Exception as e:
458 cls.logger.exception("Subprocess returned unexpected from "
459 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100460 raise
461
Klement Sekera277b89c2016-10-28 13:20:27 +0200462 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100463
Damjan Marionf56b77a2016-10-03 19:44:57 +0200464 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200465 def wait_for_stats_socket(cls):
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000466 deadline = time.time() + 300
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800467 ok = False
468 while time.time() < deadline or \
469 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200470 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800471 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200472 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700473 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800474 if not ok:
475 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200476
477 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000478 def wait_for_coredump(cls):
479 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400480 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000481 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400482 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000483 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400484 ok = False
485 while time.time() < deadline:
486 cls.sleep(1)
487 size = curr_size
488 curr_size = os.path.getsize(corefile)
489 if size == curr_size:
490 ok = True
491 break
492 if not ok:
493 cls.logger.error("Timed out waiting for coredump to complete:"
494 " %s", corefile)
495 else:
496 cls.logger.error("Coredump complete: %s, size %d",
497 corefile, curr_size)
498
499 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200500 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200501 """
502 Perform class setup before running the testcase
503 Remove shared memory files, start vpp and connect the vpp-api
504 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800505 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100506 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100507 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000508 seed = os.environ["RND_SEED"]
509 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100510 if hasattr(cls, 'parallel_handler'):
511 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100512 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700513
Klement Sekeraf62ae122016-10-11 11:47:09 +0200514 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200515 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200516 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200517 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200518 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
519 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100520 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
521 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200522 cls.file_handler.setLevel(DEBUG)
523 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700524 cls.logger.debug("--- setUpClass() for %s called ---" %
525 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200526 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200527 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200528 cls.logger.info("Temporary dir is %s, shm prefix is %s",
529 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000530 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200531 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100532 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100533 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200534 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100535 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100536 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200537 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200538 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200539 # need to catch exceptions here because if we raise, then the cleanup
540 # doesn't get called and we might end with a zombie vpp
541 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200542 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200543 cls.reporter.send_keep_alive(cls, 'setUpClass')
544 VppTestResult.current_test_case_info = TestCaseInfo(
545 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100546 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100547 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100548 cls.pump_thread_stop_flag = Event()
549 cls.pump_thread_wakeup_pipe = os.pipe()
550 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100551 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100552 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200553 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400554 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200555 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400556 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100557 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400558 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100559 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400560 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100561 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200562 cls.wait_for_stats_socket()
563 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200564 try:
565 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100566 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200567 cls.vpp_startup_failed = True
568 cls.logger.critical(
569 "VPP died shortly after startup, check the"
570 " output to standard error for possible cause")
571 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100572 try:
573 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100574 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100575 try:
576 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100577 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100578 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100579 if cls.debug_gdbserver:
580 print(colorize("You're running VPP inside gdbserver but "
581 "VPP-API connection failed, did you forget "
582 "to 'continue' VPP from within gdb?", RED))
583 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000584 except Exception as e:
585 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400586
587 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100588 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200589
Damjan Marionf56b77a2016-10-03 19:44:57 +0200590 @classmethod
591 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200592 """
593 Disconnect vpp-api, kill vpp and cleanup shared memory files
594 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200595 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
596 cls.vpp.poll()
597 if cls.vpp.returncode is None:
Dave Wallace24564332019-10-21 02:53:14 +0000598 print()
Klement Sekera277b89c2016-10-28 13:20:27 +0200599 print(double_line_delim)
600 print("VPP or GDB server is still running")
601 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800602 input("When done debugging, press ENTER to kill the "
603 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200604
juraj.linkes184870a2018-07-16 14:22:01 +0200605 # first signal that we want to stop the pump thread, then wake it up
606 if hasattr(cls, 'pump_thread_stop_flag'):
607 cls.pump_thread_stop_flag.set()
608 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100609 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100610 if hasattr(cls, 'pump_thread'):
611 cls.logger.debug("Waiting for pump thread to stop")
612 cls.pump_thread.join()
613 if hasattr(cls, 'vpp_stderr_reader_thread'):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000614 cls.logger.debug("Waiting for stdderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100615 cls.vpp_stderr_reader_thread.join()
616
Klement Sekeraf62ae122016-10-11 11:47:09 +0200617 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100618 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700619 cls.logger.debug("Disconnecting class vapi client on %s",
620 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100621 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700622 cls.logger.debug("Deleting class vapi attribute on %s",
623 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100624 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200625 cls.vpp.poll()
626 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000627 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100628 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400629 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100630 cls.logger.debug("Waiting for vpp to die")
631 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700632 cls.logger.debug("Deleting class vpp attribute on %s",
633 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200634 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200635
Klement Sekera3747c752017-04-10 06:30:17 +0200636 if cls.vpp_startup_failed:
637 stdout_log = cls.logger.info
638 stderr_log = cls.logger.critical
639 else:
640 stdout_log = cls.logger.info
641 stderr_log = cls.logger.info
642
Klement Sekerae4504c62016-12-08 10:16:41 +0100643 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200644 stdout_log(single_line_delim)
645 stdout_log('VPP output to stdout while running %s:', cls.__name__)
646 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100647 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200648 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
649 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200650 stdout_log('\n%s', vpp_output)
651 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200652
Klement Sekerae4504c62016-12-08 10:16:41 +0100653 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200654 stderr_log(single_line_delim)
655 stderr_log('VPP output to stderr while running %s:', cls.__name__)
656 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100657 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200658 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
659 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200660 stderr_log('\n%s', vpp_output)
661 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200662
Damjan Marionf56b77a2016-10-03 19:44:57 +0200663 @classmethod
664 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200665 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700666 cls.logger.debug("--- tearDownClass() for %s called ---" %
667 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200668 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200669 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200670 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100671 cls.reset_packet_infos()
672 if debug_framework:
673 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200674
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700675 def show_commands_at_teardown(self):
676 """ Allow subclass specific teardown logging additions."""
677 self.logger.info("--- No test specific show commands provided. ---")
678
Damjan Marionf56b77a2016-10-03 19:44:57 +0200679 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200680 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100681 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
682 (self.__class__.__name__, self._testMethodName,
683 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700684
685 try:
686 if not self.vpp_dead:
687 self.logger.debug(self.vapi.cli("show trace max 1000"))
688 self.logger.info(self.vapi.ppcli("show interface"))
689 self.logger.info(self.vapi.ppcli("show hardware"))
690 self.logger.info(self.statistics.set_errors_str())
691 self.logger.info(self.vapi.ppcli("show run"))
692 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400693 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700694 self.logger.info("Logging testcase specific show commands.")
695 self.show_commands_at_teardown()
696 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500697 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000698 m = self._testMethodName
699 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500700 tmp_api_trace = "/tmp/%s" % api_trace
701 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
702 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
703 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
704 vpp_api_trace_log))
705 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500706 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500707 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700708 except VppTransportShmemIOError:
709 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
710 "Cannot log show commands.")
711 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100712 else:
713 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200714
Damjan Marionf56b77a2016-10-03 19:44:57 +0200715 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200716 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800717 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200718 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100719 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400720
721 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
722 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100723 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100724 self.vpp_stdout_deque.append(
725 "--- test setUp() for %s.%s(%s) starts here ---\n" %
726 (self.__class__.__name__, self._testMethodName,
727 self._testMethodDoc))
728 self.vpp_stderr_deque.append(
729 "--- test setUp() for %s.%s(%s) starts here ---\n" %
730 (self.__class__.__name__, self._testMethodName,
731 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200732 self.vapi.cli("clear trace")
733 # store the test instance inside the test class - so that objects
734 # holding the class can access instance methods (like assertEqual)
735 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736
Damjan Marionf56b77a2016-10-03 19:44:57 +0200737 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200738 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200739 """
740 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200741
Klement Sekera75e7d132017-09-20 08:26:30 +0200742 :param interfaces: iterable interface indexes (if None,
743 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200744
Klement Sekeraf62ae122016-10-11 11:47:09 +0200745 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200746 if interfaces is None:
747 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200748 for i in interfaces:
749 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200750
Damjan Marionf56b77a2016-10-03 19:44:57 +0200751 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100752 def register_capture(cls, cap_name):
753 """ Register a capture in the testclass """
754 # add to the list of captures with current timestamp
755 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100756
757 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000758 def get_vpp_time(cls):
759 return float(cls.vapi.cli('show clock').replace("Time now ", ""))
760
761 @classmethod
762 def sleep_on_vpp_time(cls, sec):
763 """ Sleep according to time in VPP world """
764 # On a busy system with many processes
765 # we might end up with VPP time being slower than real world
766 # So take that into account when waiting for VPP to do something
767 start_time = cls.get_vpp_time()
768 while cls.get_vpp_time() - start_time < sec:
769 cls.sleep(0.1)
770
771 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100772 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000773 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100774 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200775 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000776 # PG, when starts, runs to completion -
777 # so let's avoid a race condition,
778 # and wait a little till it's done.
779 # Then clean it up - and then be gone.
780 deadline = time.time() + 300
781 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
782 cls.sleep(0.01) # yield
783 if time.time() > deadline:
784 cls.logger.error("Timeout waiting for pg to stop")
785 break
786 for stamp, cap_name in cls._captures:
787 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100788 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200789
Damjan Marionf56b77a2016-10-03 19:44:57 +0200790 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200791 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200792 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100793 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200794
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100795 :param interfaces: iterable indexes of the interfaces.
796 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200797
Klement Sekeraf62ae122016-10-11 11:47:09 +0200798 """
799 result = []
800 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200801 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200802 setattr(cls, intf.name, intf)
803 result.append(intf)
804 cls.pg_interfaces = result
805 return result
806
Matej Klotton0178d522016-11-04 11:11:44 +0100807 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200808 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100809 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100810 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100811
Klement Sekerab9ef2732018-06-24 22:49:33 +0200812 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100813 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100814 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200815 result = [VppLoInterface(cls) for i in range(count)]
816 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100817 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100818 cls.lo_interfaces = result
819 return result
820
Neale Ranns192b13f2019-03-15 02:16:20 -0700821 @classmethod
822 def create_bvi_interfaces(cls, count):
823 """
824 Create BVI interfaces.
825
826 :param count: number of interfaces created.
827 :returns: List of created interfaces.
828 """
829 result = [VppBviInterface(cls) for i in range(count)]
830 for intf in result:
831 setattr(cls, intf.name, intf)
832 cls.bvi_interfaces = result
833 return result
834
Damjan Marionf56b77a2016-10-03 19:44:57 +0200835 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200836 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200837 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200838 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200839 NOTE: Currently works only when Raw layer is present.
840
841 :param packet: packet
842 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200843 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200844
845 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200846 packet_len = len(packet) + 4
847 extend = size - packet_len
848 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200849 num = (extend // len(padding)) + 1
850 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200851
Klement Sekeradab231a2016-12-21 08:50:14 +0100852 @classmethod
853 def reset_packet_infos(cls):
854 """ Reset the list of packet info objects and packet counts to zero """
855 cls._packet_infos = {}
856 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200857
Klement Sekeradab231a2016-12-21 08:50:14 +0100858 @classmethod
859 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200860 """
861 Create packet info object containing the source and destination indexes
862 and add it to the testcase's packet info list
863
Klement Sekeradab231a2016-12-21 08:50:14 +0100864 :param VppInterface src_if: source interface
865 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200866
867 :returns: _PacketInfo object
868
869 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200870 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100871 info.index = len(cls._packet_infos)
872 info.src = src_if.sw_if_index
873 info.dst = dst_if.sw_if_index
874 if isinstance(dst_if, VppSubInterface):
875 dst_idx = dst_if.parent.sw_if_index
876 else:
877 dst_idx = dst_if.sw_if_index
878 if dst_idx in cls._packet_count_for_dst_if_idx:
879 cls._packet_count_for_dst_if_idx[dst_idx] += 1
880 else:
881 cls._packet_count_for_dst_if_idx[dst_idx] = 1
882 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200883 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200884
Damjan Marionf56b77a2016-10-03 19:44:57 +0200885 @staticmethod
886 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200887 """
888 Convert _PacketInfo object to packet payload
889
890 :param info: _PacketInfo object
891
892 :returns: string containing serialized data from packet info
893 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100894 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
895 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200896
Damjan Marionf56b77a2016-10-03 19:44:57 +0200897 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800898 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200899 """
900 Convert packet payload to _PacketInfo object
901
902 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700903 :type payload: <class 'scapy.packet.Raw'>
904 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800905 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700906 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200907 :returns: _PacketInfo object containing de-serialized data from payload
908
909 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800910 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200911 info = _PacketInfo()
912 info.index = int(numbers[0])
913 info.src = int(numbers[1])
914 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100915 info.ip = int(numbers[3])
916 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200917 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200918
Damjan Marionf56b77a2016-10-03 19:44:57 +0200919 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200920 """
921 Iterate over the packet info list stored in the testcase
922 Start iteration with first element if info is None
923 Continue based on index in info if info is specified
924
925 :param info: info or None
926 :returns: next info in list or None if no more infos
927 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200928 if info is None:
929 next_index = 0
930 else:
931 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100932 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200933 return None
934 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100935 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200936
Klement Sekeraf62ae122016-10-11 11:47:09 +0200937 def get_next_packet_info_for_interface(self, src_index, info):
938 """
939 Search the packet info list for the next packet info with same source
940 interface index
941
942 :param src_index: source interface index to search for
943 :param info: packet info - where to start the search
944 :returns: packet info or None
945
946 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200947 while True:
948 info = self.get_next_packet_info(info)
949 if info is None:
950 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200951 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200952 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200953
Klement Sekeraf62ae122016-10-11 11:47:09 +0200954 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
955 """
956 Search the packet info list for the next packet info with same source
957 and destination interface indexes
958
959 :param src_index: source interface index to search for
960 :param dst_index: destination interface index to search for
961 :param info: packet info - where to start the search
962 :returns: packet info or None
963
964 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200965 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200966 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200967 if info is None:
968 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200969 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200970 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200971
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200972 def assert_equal(self, real_value, expected_value, name_or_class=None):
973 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100974 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200975 return
976 try:
977 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
978 msg = msg % (getdoc(name_or_class).strip(),
979 real_value, str(name_or_class(real_value)),
980 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100981 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200982 msg = "Invalid %s: %s does not match expected value %s" % (
983 name_or_class, real_value, expected_value)
984
985 self.assertEqual(real_value, expected_value, msg)
986
Klement Sekerab17dd962017-01-09 07:43:48 +0100987 def assert_in_range(self,
988 real_value,
989 expected_min,
990 expected_max,
991 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200992 if name is None:
993 msg = None
994 else:
995 msg = "Invalid %s: %s out of range <%s,%s>" % (
996 name, real_value, expected_min, expected_max)
997 self.assertTrue(expected_min <= real_value <= expected_max, msg)
998
Klement Sekerad81ae412018-05-16 10:52:54 +0200999 def assert_packet_checksums_valid(self, packet,
1000 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001001 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001002 udp_layers = ['UDP', 'UDPerror']
1003 checksum_fields = ['cksum', 'chksum']
1004 checksums = []
1005 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001006 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001007 while True:
1008 layer = temp.getlayer(counter)
1009 if layer:
1010 for cf in checksum_fields:
1011 if hasattr(layer, cf):
1012 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001013 0 == getattr(layer, cf) and \
1014 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001015 continue
1016 delattr(layer, cf)
1017 checksums.append((counter, cf))
1018 else:
1019 break
1020 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001021 if 0 == len(checksums):
1022 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001023 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001024 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001025 calc_sum = getattr(temp[layer], cf)
1026 self.assert_equal(
1027 getattr(received[layer], cf), calc_sum,
1028 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1029 self.logger.debug(
1030 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1031 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001032
1033 def assert_checksum_valid(self, received_packet, layer,
1034 field_name='chksum',
1035 ignore_zero_checksum=False):
1036 """ Check checksum of received packet on given layer """
1037 received_packet_checksum = getattr(received_packet[layer], field_name)
1038 if ignore_zero_checksum and 0 == received_packet_checksum:
1039 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001040 recalculated = received_packet.__class__(
1041 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001042 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001043 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001044 self.assert_equal(received_packet_checksum,
1045 getattr(recalculated[layer], field_name),
1046 "packet checksum on layer: %s" % layer)
1047
1048 def assert_ip_checksum_valid(self, received_packet,
1049 ignore_zero_checksum=False):
1050 self.assert_checksum_valid(received_packet, 'IP',
1051 ignore_zero_checksum=ignore_zero_checksum)
1052
1053 def assert_tcp_checksum_valid(self, received_packet,
1054 ignore_zero_checksum=False):
1055 self.assert_checksum_valid(received_packet, 'TCP',
1056 ignore_zero_checksum=ignore_zero_checksum)
1057
1058 def assert_udp_checksum_valid(self, received_packet,
1059 ignore_zero_checksum=True):
1060 self.assert_checksum_valid(received_packet, 'UDP',
1061 ignore_zero_checksum=ignore_zero_checksum)
1062
1063 def assert_embedded_icmp_checksum_valid(self, received_packet):
1064 if received_packet.haslayer(IPerror):
1065 self.assert_checksum_valid(received_packet, 'IPerror')
1066 if received_packet.haslayer(TCPerror):
1067 self.assert_checksum_valid(received_packet, 'TCPerror')
1068 if received_packet.haslayer(UDPerror):
1069 self.assert_checksum_valid(received_packet, 'UDPerror',
1070 ignore_zero_checksum=True)
1071 if received_packet.haslayer(ICMPerror):
1072 self.assert_checksum_valid(received_packet, 'ICMPerror')
1073
1074 def assert_icmp_checksum_valid(self, received_packet):
1075 self.assert_checksum_valid(received_packet, 'ICMP')
1076 self.assert_embedded_icmp_checksum_valid(received_packet)
1077
1078 def assert_icmpv6_checksum_valid(self, pkt):
1079 if pkt.haslayer(ICMPv6DestUnreach):
1080 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1081 self.assert_embedded_icmp_checksum_valid(pkt)
1082 if pkt.haslayer(ICMPv6EchoRequest):
1083 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1084 if pkt.haslayer(ICMPv6EchoReply):
1085 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1086
Klement Sekera3a343d42019-05-16 14:35:46 +02001087 def get_packet_counter(self, counter):
1088 if counter.startswith("/"):
1089 counter_value = self.statistics.get_counter(counter)
1090 else:
1091 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001092 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001093 for i in range(1, len(counters) - 1):
1094 results = counters[i].split()
1095 if results[1] == counter:
1096 counter_value = int(results[0])
1097 break
1098 return counter_value
1099
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001100 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001101 counter_value = self.get_packet_counter(counter)
1102 self.assert_equal(counter_value, expected_value,
1103 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001104
Ole Troan233e4682019-05-16 15:01:34 +02001105 def assert_error_counter_equal(self, counter, expected_value):
1106 counter_value = self.statistics.get_err_counter(counter)
1107 self.assert_equal(counter_value, expected_value,
1108 "error counter `%s'" % counter)
1109
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001110 @classmethod
1111 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001112
1113 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1114 # * by Guido, only the main thread can be interrupted.
1115 # */
1116 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1117 if timeout == 0:
1118 # yield quantum
1119 if hasattr(os, 'sched_yield'):
1120 os.sched_yield()
1121 else:
1122 time.sleep(0)
1123 return
1124
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001125 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001126 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001127 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001128 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001129 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001130 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001131 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001132 "slept for %es instead of ~%es!",
1133 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001134 if hasattr(cls, 'logger'):
1135 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001136 "Finished sleep (%s) - slept %es (wanted %es)",
1137 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001138
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001139 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001140 self.vapi.cli("clear trace")
1141 intf.add_stream(pkts)
1142 self.pg_enable_capture(self.pg_interfaces)
1143 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001144
1145 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1146 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001147 if not timeout:
1148 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001149 for i in self.pg_interfaces:
1150 i.get_capture(0, timeout=timeout)
1151 i.assert_nothing_captured(remark=remark)
1152 timeout = 0.1
1153
Neale Rannsd7603d92019-03-28 08:56:10 +00001154 def send_and_expect(self, intf, pkts, output, n_rx=None):
1155 if not n_rx:
1156 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001157 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001158 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001159 return rx
1160
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001161 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1162 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001163 rx = output.get_capture(len(pkts))
1164 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001165 if not timeout:
1166 timeout = 1
1167 for i in self.pg_interfaces:
1168 if i not in outputs:
1169 i.get_capture(0, timeout=timeout)
1170 i.assert_nothing_captured()
1171 timeout = 0.1
1172
Neale Ranns52fae862018-01-08 04:41:42 -08001173 return rx
1174
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001175 def runTest(self):
1176 """ unittest calls runTest when TestCase is instantiated without a
1177 test case. Use case: Writing unittests against VppTestCase"""
1178 pass
1179
Damjan Marionf56b77a2016-10-03 19:44:57 +02001180
juraj.linkes184870a2018-07-16 14:22:01 +02001181def get_testcase_doc_name(test):
1182 return getdoc(test.__class__).splitlines()[0]
1183
1184
Ole Trøan5ba91592018-11-22 10:01:09 +00001185def get_test_description(descriptions, test):
1186 short_description = test.shortDescription()
1187 if descriptions and short_description:
1188 return short_description
1189 else:
1190 return str(test)
1191
1192
juraj.linkes40dd73b2018-09-21 13:55:16 +02001193class TestCaseInfo(object):
1194 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1195 self.logger = logger
1196 self.tempdir = tempdir
1197 self.vpp_pid = vpp_pid
1198 self.vpp_bin_path = vpp_bin_path
1199 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001200
1201
Damjan Marionf56b77a2016-10-03 19:44:57 +02001202class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001203 """
1204 @property result_string
1205 String variable to store the test case result string.
1206 @property errors
1207 List variable containing 2-tuples of TestCase instances and strings
1208 holding formatted tracebacks. Each tuple represents a test which
1209 raised an unexpected exception.
1210 @property failures
1211 List variable containing 2-tuples of TestCase instances and strings
1212 holding formatted tracebacks. Each tuple represents a test where
1213 a failure was explicitly signalled using the TestCase.assert*()
1214 methods.
1215 """
1216
juraj.linkes40dd73b2018-09-21 13:55:16 +02001217 failed_test_cases_info = set()
1218 core_crash_test_cases_info = set()
1219 current_test_case_info = None
1220
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001221 def __init__(self, stream=None, descriptions=None, verbosity=None,
1222 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001223 """
Klement Sekerada505f62017-01-04 12:58:53 +01001224 :param stream File descriptor to store where to report test results.
1225 Set to the standard error stream by default.
1226 :param descriptions Boolean variable to store information if to use
1227 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001228 :param verbosity Integer variable to store required verbosity level.
1229 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001230 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001231 self.stream = stream
1232 self.descriptions = descriptions
1233 self.verbosity = verbosity
1234 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001235 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001236
Damjan Marionf56b77a2016-10-03 19:44:57 +02001237 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001238 """
1239 Record a test succeeded result
1240
1241 :param test:
1242
1243 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001244 if self.current_test_case_info:
1245 self.current_test_case_info.logger.debug(
1246 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1247 test._testMethodName,
1248 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001249 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001250 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001251
juraj.linkescae64f82018-09-19 15:01:47 +02001252 self.send_result_through_pipe(test, PASS)
1253
Klement Sekeraf62ae122016-10-11 11:47:09 +02001254 def addSkip(self, test, reason):
1255 """
1256 Record a test skipped.
1257
1258 :param test:
1259 :param reason:
1260
1261 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001262 if self.current_test_case_info:
1263 self.current_test_case_info.logger.debug(
1264 "--- addSkip() %s.%s(%s) called, reason is %s" %
1265 (test.__class__.__name__, test._testMethodName,
1266 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001267 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001268 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001269
juraj.linkescae64f82018-09-19 15:01:47 +02001270 self.send_result_through_pipe(test, SKIP)
1271
juraj.linkes40dd73b2018-09-21 13:55:16 +02001272 def symlink_failed(self):
1273 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001274 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001275 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001276 link_path = os.path.join(
1277 failed_dir,
1278 '%s-FAILED' %
1279 os.path.basename(self.current_test_case_info.tempdir))
1280 if self.current_test_case_info.logger:
1281 self.current_test_case_info.logger.debug(
1282 "creating a link to the failed test")
1283 self.current_test_case_info.logger.debug(
1284 "os.symlink(%s, %s)" %
1285 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001286 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001287 if self.current_test_case_info.logger:
1288 self.current_test_case_info.logger.debug(
1289 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001290 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001291 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001292
Klement Sekeraf413bef2017-08-15 07:09:02 +02001293 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001294 if self.current_test_case_info.logger:
1295 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001296
juraj.linkescae64f82018-09-19 15:01:47 +02001297 def send_result_through_pipe(self, test, result):
1298 if hasattr(self, 'test_framework_result_pipe'):
1299 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001300 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001301 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001302
juraj.linkes40dd73b2018-09-21 13:55:16 +02001303 def log_error(self, test, err, fn_name):
1304 if self.current_test_case_info:
1305 if isinstance(test, unittest.suite._ErrorHolder):
1306 test_name = test.description
1307 else:
1308 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1309 test._testMethodName,
1310 test._testMethodDoc)
1311 self.current_test_case_info.logger.debug(
1312 "--- %s() %s called, err is %s" %
1313 (fn_name, test_name, err))
1314 self.current_test_case_info.logger.debug(
1315 "formatted exception is:\n%s" %
1316 "".join(format_exception(*err)))
1317
1318 def add_error(self, test, err, unittest_fn, error_type):
1319 if error_type == FAIL:
1320 self.log_error(test, err, 'addFailure')
1321 error_type_str = colorize("FAIL", RED)
1322 elif error_type == ERROR:
1323 self.log_error(test, err, 'addError')
1324 error_type_str = colorize("ERROR", RED)
1325 else:
1326 raise Exception('Error type %s cannot be used to record an '
1327 'error or a failure' % error_type)
1328
1329 unittest_fn(self, test, err)
1330 if self.current_test_case_info:
1331 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1332 (error_type_str,
1333 self.current_test_case_info.tempdir)
1334 self.symlink_failed()
1335 self.failed_test_cases_info.add(self.current_test_case_info)
1336 if is_core_present(self.current_test_case_info.tempdir):
1337 if not self.current_test_case_info.core_crash_test:
1338 if isinstance(test, unittest.suite._ErrorHolder):
1339 test_name = str(test)
1340 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001341 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001342 get_testcase_doc_name(test), test.id())
1343 self.current_test_case_info.core_crash_test = test_name
1344 self.core_crash_test_cases_info.add(
1345 self.current_test_case_info)
1346 else:
1347 self.result_string = '%s [no temp dir]' % error_type_str
1348
1349 self.send_result_through_pipe(test, error_type)
1350
Damjan Marionf56b77a2016-10-03 19:44:57 +02001351 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001352 """
1353 Record a test failed result
1354
1355 :param test:
1356 :param err: error message
1357
1358 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001359 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001360
Damjan Marionf56b77a2016-10-03 19:44:57 +02001361 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001362 """
1363 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001364
Klement Sekeraf62ae122016-10-11 11:47:09 +02001365 :param test:
1366 :param err: error message
1367
1368 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001369 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001370
Damjan Marionf56b77a2016-10-03 19:44:57 +02001371 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001372 """
1373 Get test description
1374
1375 :param test:
1376 :returns: test description
1377
1378 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001379 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001380
Damjan Marionf56b77a2016-10-03 19:44:57 +02001381 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001382 """
1383 Start a test
1384
1385 :param test:
1386
1387 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001388
1389 def print_header(test):
1390 if not hasattr(test.__class__, '_header_printed'):
1391 print(double_line_delim)
1392 print(colorize(getdoc(test).splitlines()[0], GREEN))
1393 print(double_line_delim)
1394 test.__class__._header_printed = True
1395
1396 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001397
Damjan Marionf56b77a2016-10-03 19:44:57 +02001398 unittest.TestResult.startTest(self, test)
1399 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001400 self.stream.writeln(
1401 "Starting " + self.getDescription(test) + " ...")
1402 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001403
Damjan Marionf56b77a2016-10-03 19:44:57 +02001404 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001405 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001406 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001407
1408 :param test:
1409
1410 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001411 unittest.TestResult.stopTest(self, test)
1412 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001413 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001414 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001415 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001416 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001417 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001418 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001419 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001420
1421 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001422
Damjan Marionf56b77a2016-10-03 19:44:57 +02001423 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001424 """
1425 Print errors from running the test case
1426 """
juraj.linkesabec0122018-11-16 17:28:56 +01001427 if len(self.errors) > 0 or len(self.failures) > 0:
1428 self.stream.writeln()
1429 self.printErrorList('ERROR', self.errors)
1430 self.printErrorList('FAIL', self.failures)
1431
1432 # ^^ that is the last output from unittest before summary
1433 if not self.runner.print_summary:
1434 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1435 self.stream = devnull
1436 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001437
Damjan Marionf56b77a2016-10-03 19:44:57 +02001438 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001439 """
1440 Print error list to the output stream together with error type
1441 and test case description.
1442
1443 :param flavour: error type
1444 :param errors: iterable errors
1445
1446 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001447 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001448 self.stream.writeln(double_line_delim)
1449 self.stream.writeln("%s: %s" %
1450 (flavour, self.getDescription(test)))
1451 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001452 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001453
1454
Damjan Marionf56b77a2016-10-03 19:44:57 +02001455class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001456 """
Klement Sekera104543f2017-02-03 07:29:43 +01001457 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001458 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001459
Klement Sekeraf62ae122016-10-11 11:47:09 +02001460 @property
1461 def resultclass(self):
1462 """Class maintaining the results of the tests"""
1463 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001464
juraj.linkes184870a2018-07-16 14:22:01 +02001465 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001466 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001467 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001468 # ignore stream setting here, use hard-coded stdout to be in sync
1469 # with prints from VppTestCase methods ...
1470 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1471 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001472 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001473 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001474
juraj.linkesabec0122018-11-16 17:28:56 +01001475 self.orig_stream = self.stream
1476 self.resultclass.test_framework_result_pipe = result_pipe
1477
1478 self.print_summary = print_summary
1479
1480 def _makeResult(self):
1481 return self.resultclass(self.stream,
1482 self.descriptions,
1483 self.verbosity,
1484 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001485
Damjan Marionf56b77a2016-10-03 19:44:57 +02001486 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001487 """
1488 Run the tests
1489
1490 :param test:
1491
1492 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001493 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001494
1495 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001496 if not self.print_summary:
1497 self.stream = self.orig_stream
1498 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001499 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001500
1501
1502class Worker(Thread):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001503 def __init__(self, args, logger, env=None):
Neale Ranns812ed392017-10-16 04:20:13 -07001504 self.logger = logger
1505 self.args = args
Dave Wallace24564332019-10-21 02:53:14 +00001506 if hasattr(self, 'testcase') and self.testcase.debug_all:
1507 if self.testcase.debug_gdbserver:
1508 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1509 .format(port=self.testcase.gdbserver_port)] + args
1510 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1511 self.args.append(self.wait_for_gdb)
1512 self.app_bin = args[0]
1513 self.app_name = os.path.basename(self.app_bin)
1514 if hasattr(self, 'role'):
1515 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001516 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001517 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001518 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001519 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001520 super(Worker, self).__init__()
1521
Dave Wallace24564332019-10-21 02:53:14 +00001522 def wait_for_enter(self):
1523 if not hasattr(self, 'testcase'):
1524 return
1525 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1526 print()
1527 print(double_line_delim)
1528 print("Spawned GDB Server for '{app}' with PID: {pid}"
1529 .format(app=self.app_name, pid=self.process.pid))
1530 elif self.testcase.debug_all and self.testcase.debug_gdb:
1531 print()
1532 print(double_line_delim)
1533 print("Spawned '{app}' with PID: {pid}"
1534 .format(app=self.app_name, pid=self.process.pid))
1535 else:
1536 return
1537 print(single_line_delim)
1538 print("You can debug '{app}' using:".format(app=self.app_name))
1539 if self.testcase.debug_gdbserver:
1540 print("sudo gdb " + self.app_bin +
1541 " -ex 'target remote localhost:{port}'"
1542 .format(port=self.testcase.gdbserver_port))
1543 print("Now is the time to attach gdb by running the above "
1544 "command, set up breakpoints etc., then resume from "
1545 "within gdb by issuing the 'continue' command")
1546 self.testcase.gdbserver_port += 1
1547 elif self.testcase.debug_gdb:
1548 print("sudo gdb " + self.app_bin +
1549 " -ex 'attach {pid}'".format(pid=self.process.pid))
1550 print("Now is the time to attach gdb by running the above "
1551 "command and set up breakpoints etc., then resume from"
1552 " within gdb by issuing the 'continue' command")
1553 print(single_line_delim)
1554 input("Press ENTER to continue running the testcase...")
1555
Neale Ranns812ed392017-10-16 04:20:13 -07001556 def run(self):
1557 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001558 if not os.path.exists(executable) or not os.access(
1559 executable, os.F_OK | os.X_OK):
1560 # Exit code that means some system file did not exist,
1561 # could not be opened, or had some other kind of error.
1562 self.result = os.EX_OSFILE
1563 raise EnvironmentError(
1564 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001565 self.logger.debug("Running executable: '{app}'"
1566 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001567 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001568 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001569 env["CK_LOG_FILE_NAME"] = "-"
1570 self.process = subprocess.Popen(
1571 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1572 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001573 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001574 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001575 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001576 self.logger.info("Return code is `%s'" % self.process.returncode)
1577 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001578 self.logger.info("Executable `{app}' wrote to stdout:"
1579 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001580 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001581 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001582 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001583 self.logger.info("Executable `{app}' wrote to stderr:"
1584 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001585 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001586 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001587 self.logger.info(single_line_delim)
1588 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001589
Klement Sekera6aa58b72019-05-16 14:34:55 +02001590
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001591if __name__ == '__main__':
1592 pass