blob: 7d1955f5c7af3b43fa36ffb2e32cbf3d1ec4110c [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, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200384 "statseg", "{", "socket-name", cls.stats_sock, "}",
385 "socksvr", "{", "socket-name", cls.api_sock, "}",
386 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100387 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200388 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100389 "}", "plugin", "unittest_plugin.so", "{", "enable",
390 "}"] + cls.extra_vpp_plugin_config + ["}", ]
391 if cls.extra_vpp_punt_config is not None:
392 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100393 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100394 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400395 if cls.test_plugin_path is not None:
396 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
397
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100398 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
399 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200400
401 @classmethod
402 def wait_for_enter(cls):
403 if cls.debug_gdbserver:
404 print(double_line_delim)
405 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
406 elif cls.debug_gdb:
407 print(double_line_delim)
408 print("Spawned VPP with PID: %d" % cls.vpp.pid)
409 else:
410 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
411 return
412 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000413 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200414 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400415 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000416 " -ex 'target remote localhost:{port}'"
417 .format(port=cls.gdbserver_port))
418 print("Now is the time to attach gdb by running the above "
419 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200420 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000421 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200422 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400423 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000424 print("Now is the time to attach gdb by running the above "
425 "command and set up breakpoints etc., then resume VPP from"
426 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200427 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800428 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200429
430 @classmethod
431 def run_vpp(cls):
432 cmdline = cls.vpp_cmdline
433
434 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100435 gdbserver = '/usr/bin/gdbserver'
436 if not os.path.isfile(gdbserver) or \
437 not os.access(gdbserver, os.X_OK):
438 raise Exception("gdbserver binary '%s' does not exist or is "
439 "not executable" % gdbserver)
440
Dave Wallace24564332019-10-21 02:53:14 +0000441 cmdline = [gdbserver, 'localhost:{port}'
442 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200443 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
444
Klement Sekera931be3a2016-11-03 05:36:01 +0100445 try:
446 cls.vpp = subprocess.Popen(cmdline,
447 stdout=subprocess.PIPE,
448 stderr=subprocess.PIPE,
449 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800450 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800451 cls.logger.critical("Subprocess returned with non-0 return code: ("
452 "%s)", e.returncode)
453 raise
454 except OSError as e:
455 cls.logger.critical("Subprocess returned with OS error: "
456 "(%s) %s", e.errno, e.strerror)
457 raise
458 except Exception as e:
459 cls.logger.exception("Subprocess returned unexpected from "
460 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100461 raise
462
Klement Sekera277b89c2016-10-28 13:20:27 +0200463 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100464
Damjan Marionf56b77a2016-10-03 19:44:57 +0200465 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000466 def wait_for_coredump(cls):
467 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400468 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000469 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400470 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000471 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400472 ok = False
473 while time.time() < deadline:
474 cls.sleep(1)
475 size = curr_size
476 curr_size = os.path.getsize(corefile)
477 if size == curr_size:
478 ok = True
479 break
480 if not ok:
481 cls.logger.error("Timed out waiting for coredump to complete:"
482 " %s", corefile)
483 else:
484 cls.logger.error("Coredump complete: %s, size %d",
485 corefile, curr_size)
486
487 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200488 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200489 """
490 Perform class setup before running the testcase
491 Remove shared memory files, start vpp and connect the vpp-api
492 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800493 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100494 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100495 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000496 seed = os.environ["RND_SEED"]
497 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100498 if hasattr(cls, 'parallel_handler'):
499 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100500 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700501
Klement Sekeraf62ae122016-10-11 11:47:09 +0200502 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200503 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200504 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200505 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200506 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
507 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100508 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
509 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200510 cls.file_handler.setLevel(DEBUG)
511 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700512 cls.logger.debug("--- setUpClass() for %s called ---" %
513 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200514 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200515 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200516 cls.logger.info("Temporary dir is %s, shm prefix is %s",
517 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000518 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200519 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100520 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100521 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200522 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100523 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100524 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200525 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200526 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200527 # need to catch exceptions here because if we raise, then the cleanup
528 # doesn't get called and we might end with a zombie vpp
529 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200530 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200531 cls.reporter.send_keep_alive(cls, 'setUpClass')
532 VppTestResult.current_test_case_info = TestCaseInfo(
533 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100534 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100535 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100536 cls.pump_thread_stop_flag = Event()
537 cls.pump_thread_wakeup_pipe = os.pipe()
538 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100539 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100540 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200541 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400542 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200543 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400544 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100545 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400546 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100547 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400548 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100549 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200550 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200551 try:
552 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100553 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200554 cls.vpp_startup_failed = True
555 cls.logger.critical(
556 "VPP died shortly after startup, check the"
557 " output to standard error for possible cause")
558 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100559 try:
560 cls.vapi.connect()
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500561 except vpp_papi.VPPIOError as e:
562 cls.logger.debug("Exception connecting to vapi: %s" % e)
563 cls.vapi.disconnect()
564
Klement Sekera085f5c02016-11-24 01:59:16 +0100565 if cls.debug_gdbserver:
566 print(colorize("You're running VPP inside gdbserver but "
567 "VPP-API connection failed, did you forget "
568 "to 'continue' VPP from within gdb?", RED))
569 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000570 except Exception as e:
571 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400572
573 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100574 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200575
Damjan Marionf56b77a2016-10-03 19:44:57 +0200576 @classmethod
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500577 def _debug_quit(cls):
578 if (cls.debug_gdbserver or cls.debug_gdb):
579 try:
580 cls.vpp.poll()
581
582 if cls.vpp.returncode is None:
583 print()
584 print(double_line_delim)
585 print("VPP or GDB server is still running")
586 print(single_line_delim)
587 input("When done debugging, press ENTER to kill the "
588 "process and finish running the testcase...")
589 except AttributeError:
590 pass
591
592 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200593 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200594 """
595 Disconnect vpp-api, kill vpp and cleanup shared memory files
596 """
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500597 cls._debug_quit()
Klement Sekera277b89c2016-10-28 13:20:27 +0200598
juraj.linkes184870a2018-07-16 14:22:01 +0200599 # first signal that we want to stop the pump thread, then wake it up
600 if hasattr(cls, 'pump_thread_stop_flag'):
601 cls.pump_thread_stop_flag.set()
602 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100603 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100604 if hasattr(cls, 'pump_thread'):
605 cls.logger.debug("Waiting for pump thread to stop")
606 cls.pump_thread.join()
607 if hasattr(cls, 'vpp_stderr_reader_thread'):
Paul Vinciguerrac701e572019-12-19 16:09:43 -0500608 cls.logger.debug("Waiting for stderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100609 cls.vpp_stderr_reader_thread.join()
610
Klement Sekeraf62ae122016-10-11 11:47:09 +0200611 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100612 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100613 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700614 cls.logger.debug("Disconnecting class vapi client on %s",
615 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100616 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700617 cls.logger.debug("Deleting class vapi attribute on %s",
618 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100619 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200620 cls.vpp.poll()
621 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000622 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100623 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400624 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100625 cls.logger.debug("Waiting for vpp to die")
626 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700627 cls.logger.debug("Deleting class vpp attribute on %s",
628 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200629 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200630
Klement Sekera3747c752017-04-10 06:30:17 +0200631 if cls.vpp_startup_failed:
632 stdout_log = cls.logger.info
633 stderr_log = cls.logger.critical
634 else:
635 stdout_log = cls.logger.info
636 stderr_log = cls.logger.info
637
Klement Sekerae4504c62016-12-08 10:16:41 +0100638 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200639 stdout_log(single_line_delim)
640 stdout_log('VPP output to stdout while running %s:', cls.__name__)
641 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100642 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200643 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
644 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200645 stdout_log('\n%s', vpp_output)
646 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200647
Klement Sekerae4504c62016-12-08 10:16:41 +0100648 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200649 stderr_log(single_line_delim)
650 stderr_log('VPP output to stderr while running %s:', cls.__name__)
651 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100652 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200653 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
654 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200655 stderr_log('\n%s', vpp_output)
656 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200657
Damjan Marionf56b77a2016-10-03 19:44:57 +0200658 @classmethod
659 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200660 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700661 cls.logger.debug("--- tearDownClass() for %s called ---" %
662 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200663 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200664 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200665 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100666 cls.reset_packet_infos()
667 if debug_framework:
668 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200669
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700670 def show_commands_at_teardown(self):
671 """ Allow subclass specific teardown logging additions."""
672 self.logger.info("--- No test specific show commands provided. ---")
673
Damjan Marionf56b77a2016-10-03 19:44:57 +0200674 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200675 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100676 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
677 (self.__class__.__name__, self._testMethodName,
678 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700679
680 try:
681 if not self.vpp_dead:
682 self.logger.debug(self.vapi.cli("show trace max 1000"))
683 self.logger.info(self.vapi.ppcli("show interface"))
684 self.logger.info(self.vapi.ppcli("show hardware"))
685 self.logger.info(self.statistics.set_errors_str())
686 self.logger.info(self.vapi.ppcli("show run"))
687 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400688 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700689 self.logger.info("Logging testcase specific show commands.")
690 self.show_commands_at_teardown()
691 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500692 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000693 m = self._testMethodName
694 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500695 tmp_api_trace = "/tmp/%s" % api_trace
696 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
697 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
698 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
699 vpp_api_trace_log))
700 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500701 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500702 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700703 except VppTransportShmemIOError:
704 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
705 "Cannot log show commands.")
706 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100707 else:
708 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200709
Damjan Marionf56b77a2016-10-03 19:44:57 +0200710 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200711 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800712 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200713 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100714 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400715
716 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
717 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100718 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100719 self.vpp_stdout_deque.append(
720 "--- test setUp() for %s.%s(%s) starts here ---\n" %
721 (self.__class__.__name__, self._testMethodName,
722 self._testMethodDoc))
723 self.vpp_stderr_deque.append(
724 "--- test setUp() for %s.%s(%s) starts here ---\n" %
725 (self.__class__.__name__, self._testMethodName,
726 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200727 self.vapi.cli("clear trace")
728 # store the test instance inside the test class - so that objects
729 # holding the class can access instance methods (like assertEqual)
730 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200731
Damjan Marionf56b77a2016-10-03 19:44:57 +0200732 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200733 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200734 """
735 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736
Klement Sekera75e7d132017-09-20 08:26:30 +0200737 :param interfaces: iterable interface indexes (if None,
738 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200739
Klement Sekeraf62ae122016-10-11 11:47:09 +0200740 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200741 if interfaces is None:
742 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200743 for i in interfaces:
744 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200745
Damjan Marionf56b77a2016-10-03 19:44:57 +0200746 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100747 def register_capture(cls, cap_name):
748 """ Register a capture in the testclass """
749 # add to the list of captures with current timestamp
750 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100751
752 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000753 def get_vpp_time(cls):
754 return float(cls.vapi.cli('show clock').replace("Time now ", ""))
755
756 @classmethod
757 def sleep_on_vpp_time(cls, sec):
758 """ Sleep according to time in VPP world """
759 # On a busy system with many processes
760 # we might end up with VPP time being slower than real world
761 # So take that into account when waiting for VPP to do something
762 start_time = cls.get_vpp_time()
763 while cls.get_vpp_time() - start_time < sec:
764 cls.sleep(0.1)
765
766 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100767 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000768 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100769 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200770 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000771 # PG, when starts, runs to completion -
772 # so let's avoid a race condition,
773 # and wait a little till it's done.
774 # Then clean it up - and then be gone.
775 deadline = time.time() + 300
776 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
777 cls.sleep(0.01) # yield
778 if time.time() > deadline:
779 cls.logger.error("Timeout waiting for pg to stop")
780 break
781 for stamp, cap_name in cls._captures:
782 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100783 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784
Damjan Marionf56b77a2016-10-03 19:44:57 +0200785 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200786 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200787 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100788 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200789
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100790 :param interfaces: iterable indexes of the interfaces.
791 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200792
Klement Sekeraf62ae122016-10-11 11:47:09 +0200793 """
794 result = []
795 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200796 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200797 setattr(cls, intf.name, intf)
798 result.append(intf)
799 cls.pg_interfaces = result
800 return result
801
Matej Klotton0178d522016-11-04 11:11:44 +0100802 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200803 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100804 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100805 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100806
Klement Sekerab9ef2732018-06-24 22:49:33 +0200807 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100808 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100809 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200810 result = [VppLoInterface(cls) for i in range(count)]
811 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100812 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100813 cls.lo_interfaces = result
814 return result
815
Neale Ranns192b13f2019-03-15 02:16:20 -0700816 @classmethod
817 def create_bvi_interfaces(cls, count):
818 """
819 Create BVI interfaces.
820
821 :param count: number of interfaces created.
822 :returns: List of created interfaces.
823 """
824 result = [VppBviInterface(cls) for i in range(count)]
825 for intf in result:
826 setattr(cls, intf.name, intf)
827 cls.bvi_interfaces = result
828 return result
829
Damjan Marionf56b77a2016-10-03 19:44:57 +0200830 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200831 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200832 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200833 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200834 NOTE: Currently works only when Raw layer is present.
835
836 :param packet: packet
837 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200838 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200839
840 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200841 packet_len = len(packet) + 4
842 extend = size - packet_len
843 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200844 num = (extend // len(padding)) + 1
845 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200846
Klement Sekeradab231a2016-12-21 08:50:14 +0100847 @classmethod
848 def reset_packet_infos(cls):
849 """ Reset the list of packet info objects and packet counts to zero """
850 cls._packet_infos = {}
851 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200852
Klement Sekeradab231a2016-12-21 08:50:14 +0100853 @classmethod
854 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200855 """
856 Create packet info object containing the source and destination indexes
857 and add it to the testcase's packet info list
858
Klement Sekeradab231a2016-12-21 08:50:14 +0100859 :param VppInterface src_if: source interface
860 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200861
862 :returns: _PacketInfo object
863
864 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200865 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100866 info.index = len(cls._packet_infos)
867 info.src = src_if.sw_if_index
868 info.dst = dst_if.sw_if_index
869 if isinstance(dst_if, VppSubInterface):
870 dst_idx = dst_if.parent.sw_if_index
871 else:
872 dst_idx = dst_if.sw_if_index
873 if dst_idx in cls._packet_count_for_dst_if_idx:
874 cls._packet_count_for_dst_if_idx[dst_idx] += 1
875 else:
876 cls._packet_count_for_dst_if_idx[dst_idx] = 1
877 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200878 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200879
Damjan Marionf56b77a2016-10-03 19:44:57 +0200880 @staticmethod
881 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200882 """
883 Convert _PacketInfo object to packet payload
884
885 :param info: _PacketInfo object
886
887 :returns: string containing serialized data from packet info
888 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100889 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
890 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200891
Damjan Marionf56b77a2016-10-03 19:44:57 +0200892 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800893 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200894 """
895 Convert packet payload to _PacketInfo object
896
897 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700898 :type payload: <class 'scapy.packet.Raw'>
899 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800900 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700901 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200902 :returns: _PacketInfo object containing de-serialized data from payload
903
904 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800905 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200906 info = _PacketInfo()
907 info.index = int(numbers[0])
908 info.src = int(numbers[1])
909 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100910 info.ip = int(numbers[3])
911 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200912 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200913
Damjan Marionf56b77a2016-10-03 19:44:57 +0200914 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200915 """
916 Iterate over the packet info list stored in the testcase
917 Start iteration with first element if info is None
918 Continue based on index in info if info is specified
919
920 :param info: info or None
921 :returns: next info in list or None if no more infos
922 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200923 if info is None:
924 next_index = 0
925 else:
926 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100927 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200928 return None
929 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100930 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200931
Klement Sekeraf62ae122016-10-11 11:47:09 +0200932 def get_next_packet_info_for_interface(self, src_index, info):
933 """
934 Search the packet info list for the next packet info with same source
935 interface index
936
937 :param src_index: source interface index to search for
938 :param info: packet info - where to start the search
939 :returns: packet info or None
940
941 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200942 while True:
943 info = self.get_next_packet_info(info)
944 if info is None:
945 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200946 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200947 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200948
Klement Sekeraf62ae122016-10-11 11:47:09 +0200949 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
950 """
951 Search the packet info list for the next packet info with same source
952 and destination interface indexes
953
954 :param src_index: source interface index to search for
955 :param dst_index: destination interface index to search for
956 :param info: packet info - where to start the search
957 :returns: packet info or None
958
959 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200960 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200961 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200962 if info is None:
963 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200964 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200965 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200966
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200967 def assert_equal(self, real_value, expected_value, name_or_class=None):
968 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100969 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200970 return
971 try:
972 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
973 msg = msg % (getdoc(name_or_class).strip(),
974 real_value, str(name_or_class(real_value)),
975 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100976 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200977 msg = "Invalid %s: %s does not match expected value %s" % (
978 name_or_class, real_value, expected_value)
979
980 self.assertEqual(real_value, expected_value, msg)
981
Klement Sekerab17dd962017-01-09 07:43:48 +0100982 def assert_in_range(self,
983 real_value,
984 expected_min,
985 expected_max,
986 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200987 if name is None:
988 msg = None
989 else:
990 msg = "Invalid %s: %s out of range <%s,%s>" % (
991 name, real_value, expected_min, expected_max)
992 self.assertTrue(expected_min <= real_value <= expected_max, msg)
993
Klement Sekerad81ae412018-05-16 10:52:54 +0200994 def assert_packet_checksums_valid(self, packet,
995 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700996 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200997 udp_layers = ['UDP', 'UDPerror']
998 checksum_fields = ['cksum', 'chksum']
999 checksums = []
1000 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001001 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001002 while True:
1003 layer = temp.getlayer(counter)
1004 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001005 layer = layer.copy()
1006 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001007 for cf in checksum_fields:
1008 if hasattr(layer, cf):
1009 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001010 0 == getattr(layer, cf) and \
1011 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001012 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001013 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001014 checksums.append((counter, cf))
1015 else:
1016 break
1017 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001018 if 0 == len(checksums):
1019 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001020 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001021 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001022 calc_sum = getattr(temp[layer], cf)
1023 self.assert_equal(
1024 getattr(received[layer], cf), calc_sum,
1025 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1026 self.logger.debug(
1027 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1028 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001029
1030 def assert_checksum_valid(self, received_packet, layer,
1031 field_name='chksum',
1032 ignore_zero_checksum=False):
1033 """ Check checksum of received packet on given layer """
1034 received_packet_checksum = getattr(received_packet[layer], field_name)
1035 if ignore_zero_checksum and 0 == received_packet_checksum:
1036 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001037 recalculated = received_packet.__class__(
1038 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001039 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001040 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001041 self.assert_equal(received_packet_checksum,
1042 getattr(recalculated[layer], field_name),
1043 "packet checksum on layer: %s" % layer)
1044
1045 def assert_ip_checksum_valid(self, received_packet,
1046 ignore_zero_checksum=False):
1047 self.assert_checksum_valid(received_packet, 'IP',
1048 ignore_zero_checksum=ignore_zero_checksum)
1049
1050 def assert_tcp_checksum_valid(self, received_packet,
1051 ignore_zero_checksum=False):
1052 self.assert_checksum_valid(received_packet, 'TCP',
1053 ignore_zero_checksum=ignore_zero_checksum)
1054
1055 def assert_udp_checksum_valid(self, received_packet,
1056 ignore_zero_checksum=True):
1057 self.assert_checksum_valid(received_packet, 'UDP',
1058 ignore_zero_checksum=ignore_zero_checksum)
1059
1060 def assert_embedded_icmp_checksum_valid(self, received_packet):
1061 if received_packet.haslayer(IPerror):
1062 self.assert_checksum_valid(received_packet, 'IPerror')
1063 if received_packet.haslayer(TCPerror):
1064 self.assert_checksum_valid(received_packet, 'TCPerror')
1065 if received_packet.haslayer(UDPerror):
1066 self.assert_checksum_valid(received_packet, 'UDPerror',
1067 ignore_zero_checksum=True)
1068 if received_packet.haslayer(ICMPerror):
1069 self.assert_checksum_valid(received_packet, 'ICMPerror')
1070
1071 def assert_icmp_checksum_valid(self, received_packet):
1072 self.assert_checksum_valid(received_packet, 'ICMP')
1073 self.assert_embedded_icmp_checksum_valid(received_packet)
1074
1075 def assert_icmpv6_checksum_valid(self, pkt):
1076 if pkt.haslayer(ICMPv6DestUnreach):
1077 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1078 self.assert_embedded_icmp_checksum_valid(pkt)
1079 if pkt.haslayer(ICMPv6EchoRequest):
1080 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1081 if pkt.haslayer(ICMPv6EchoReply):
1082 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1083
Klement Sekera3a343d42019-05-16 14:35:46 +02001084 def get_packet_counter(self, counter):
1085 if counter.startswith("/"):
1086 counter_value = self.statistics.get_counter(counter)
1087 else:
1088 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001089 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001090 for i in range(1, len(counters) - 1):
1091 results = counters[i].split()
1092 if results[1] == counter:
1093 counter_value = int(results[0])
1094 break
1095 return counter_value
1096
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001097 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001098 counter_value = self.get_packet_counter(counter)
1099 self.assert_equal(counter_value, expected_value,
1100 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001101
Ole Troan233e4682019-05-16 15:01:34 +02001102 def assert_error_counter_equal(self, counter, expected_value):
1103 counter_value = self.statistics.get_err_counter(counter)
1104 self.assert_equal(counter_value, expected_value,
1105 "error counter `%s'" % counter)
1106
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001107 @classmethod
1108 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001109
1110 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1111 # * by Guido, only the main thread can be interrupted.
1112 # */
1113 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1114 if timeout == 0:
1115 # yield quantum
1116 if hasattr(os, 'sched_yield'):
1117 os.sched_yield()
1118 else:
1119 time.sleep(0)
1120 return
1121
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001122 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001123 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001124 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001125 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001126 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001127 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001128 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001129 "slept for %es instead of ~%es!",
1130 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001131 if hasattr(cls, 'logger'):
1132 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001133 "Finished sleep (%s) - slept %es (wanted %es)",
1134 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001135
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001136 def pg_send(self, intf, pkts, worker=None):
Neale Ranns52fae862018-01-08 04:41:42 -08001137 self.vapi.cli("clear trace")
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001138 intf.add_stream(pkts, worker=worker)
Neale Ranns52fae862018-01-08 04:41:42 -08001139 self.pg_enable_capture(self.pg_interfaces)
1140 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001141
1142 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1143 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001144 if not timeout:
1145 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001146 for i in self.pg_interfaces:
1147 i.get_capture(0, timeout=timeout)
1148 i.assert_nothing_captured(remark=remark)
1149 timeout = 0.1
1150
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001151 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None):
Neale Rannsd7603d92019-03-28 08:56:10 +00001152 if not n_rx:
1153 n_rx = len(pkts)
Neale Ranns4a56f4e2019-12-23 04:10:25 +00001154 self.pg_send(intf, pkts, worker=worker)
Neale Rannsd7603d92019-03-28 08:56:10 +00001155 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001156 return rx
1157
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001158 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1159 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001160 rx = output.get_capture(len(pkts))
1161 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001162 if not timeout:
1163 timeout = 1
1164 for i in self.pg_interfaces:
1165 if i not in outputs:
1166 i.get_capture(0, timeout=timeout)
1167 i.assert_nothing_captured()
1168 timeout = 0.1
1169
Neale Ranns52fae862018-01-08 04:41:42 -08001170 return rx
1171
Damjan Marionf56b77a2016-10-03 19:44:57 +02001172
juraj.linkes184870a2018-07-16 14:22:01 +02001173def get_testcase_doc_name(test):
1174 return getdoc(test.__class__).splitlines()[0]
1175
1176
Ole Trøan5ba91592018-11-22 10:01:09 +00001177def get_test_description(descriptions, test):
1178 short_description = test.shortDescription()
1179 if descriptions and short_description:
1180 return short_description
1181 else:
1182 return str(test)
1183
1184
juraj.linkes40dd73b2018-09-21 13:55:16 +02001185class TestCaseInfo(object):
1186 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1187 self.logger = logger
1188 self.tempdir = tempdir
1189 self.vpp_pid = vpp_pid
1190 self.vpp_bin_path = vpp_bin_path
1191 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001192
1193
Damjan Marionf56b77a2016-10-03 19:44:57 +02001194class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001195 """
1196 @property result_string
1197 String variable to store the test case result string.
1198 @property errors
1199 List variable containing 2-tuples of TestCase instances and strings
1200 holding formatted tracebacks. Each tuple represents a test which
1201 raised an unexpected exception.
1202 @property failures
1203 List variable containing 2-tuples of TestCase instances and strings
1204 holding formatted tracebacks. Each tuple represents a test where
1205 a failure was explicitly signalled using the TestCase.assert*()
1206 methods.
1207 """
1208
juraj.linkes40dd73b2018-09-21 13:55:16 +02001209 failed_test_cases_info = set()
1210 core_crash_test_cases_info = set()
1211 current_test_case_info = None
1212
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001213 def __init__(self, stream=None, descriptions=None, verbosity=None,
1214 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001215 """
Klement Sekerada505f62017-01-04 12:58:53 +01001216 :param stream File descriptor to store where to report test results.
1217 Set to the standard error stream by default.
1218 :param descriptions Boolean variable to store information if to use
1219 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001220 :param verbosity Integer variable to store required verbosity level.
1221 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001222 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001223 self.stream = stream
1224 self.descriptions = descriptions
1225 self.verbosity = verbosity
1226 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001227 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001228
Damjan Marionf56b77a2016-10-03 19:44:57 +02001229 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001230 """
1231 Record a test succeeded result
1232
1233 :param test:
1234
1235 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001236 if self.current_test_case_info:
1237 self.current_test_case_info.logger.debug(
1238 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1239 test._testMethodName,
1240 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001241 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001242 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001243
juraj.linkescae64f82018-09-19 15:01:47 +02001244 self.send_result_through_pipe(test, PASS)
1245
Klement Sekeraf62ae122016-10-11 11:47:09 +02001246 def addSkip(self, test, reason):
1247 """
1248 Record a test skipped.
1249
1250 :param test:
1251 :param reason:
1252
1253 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001254 if self.current_test_case_info:
1255 self.current_test_case_info.logger.debug(
1256 "--- addSkip() %s.%s(%s) called, reason is %s" %
1257 (test.__class__.__name__, test._testMethodName,
1258 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001259 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001260 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001261
juraj.linkescae64f82018-09-19 15:01:47 +02001262 self.send_result_through_pipe(test, SKIP)
1263
juraj.linkes40dd73b2018-09-21 13:55:16 +02001264 def symlink_failed(self):
1265 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001266 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001267 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001268 link_path = os.path.join(
1269 failed_dir,
1270 '%s-FAILED' %
1271 os.path.basename(self.current_test_case_info.tempdir))
1272 if self.current_test_case_info.logger:
1273 self.current_test_case_info.logger.debug(
1274 "creating a link to the failed test")
1275 self.current_test_case_info.logger.debug(
1276 "os.symlink(%s, %s)" %
1277 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001278 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001279 if self.current_test_case_info.logger:
1280 self.current_test_case_info.logger.debug(
1281 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001282 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001283 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001284
Klement Sekeraf413bef2017-08-15 07:09:02 +02001285 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001286 if self.current_test_case_info.logger:
1287 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001288
juraj.linkescae64f82018-09-19 15:01:47 +02001289 def send_result_through_pipe(self, test, result):
1290 if hasattr(self, 'test_framework_result_pipe'):
1291 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001292 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001293 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001294
juraj.linkes40dd73b2018-09-21 13:55:16 +02001295 def log_error(self, test, err, fn_name):
1296 if self.current_test_case_info:
1297 if isinstance(test, unittest.suite._ErrorHolder):
1298 test_name = test.description
1299 else:
1300 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1301 test._testMethodName,
1302 test._testMethodDoc)
1303 self.current_test_case_info.logger.debug(
1304 "--- %s() %s called, err is %s" %
1305 (fn_name, test_name, err))
1306 self.current_test_case_info.logger.debug(
1307 "formatted exception is:\n%s" %
1308 "".join(format_exception(*err)))
1309
1310 def add_error(self, test, err, unittest_fn, error_type):
1311 if error_type == FAIL:
1312 self.log_error(test, err, 'addFailure')
1313 error_type_str = colorize("FAIL", RED)
1314 elif error_type == ERROR:
1315 self.log_error(test, err, 'addError')
1316 error_type_str = colorize("ERROR", RED)
1317 else:
1318 raise Exception('Error type %s cannot be used to record an '
1319 'error or a failure' % error_type)
1320
1321 unittest_fn(self, test, err)
1322 if self.current_test_case_info:
1323 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1324 (error_type_str,
1325 self.current_test_case_info.tempdir)
1326 self.symlink_failed()
1327 self.failed_test_cases_info.add(self.current_test_case_info)
1328 if is_core_present(self.current_test_case_info.tempdir):
1329 if not self.current_test_case_info.core_crash_test:
1330 if isinstance(test, unittest.suite._ErrorHolder):
1331 test_name = str(test)
1332 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001333 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001334 get_testcase_doc_name(test), test.id())
1335 self.current_test_case_info.core_crash_test = test_name
1336 self.core_crash_test_cases_info.add(
1337 self.current_test_case_info)
1338 else:
1339 self.result_string = '%s [no temp dir]' % error_type_str
1340
1341 self.send_result_through_pipe(test, error_type)
1342
Damjan Marionf56b77a2016-10-03 19:44:57 +02001343 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001344 """
1345 Record a test failed result
1346
1347 :param test:
1348 :param err: error message
1349
1350 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001351 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001352
Damjan Marionf56b77a2016-10-03 19:44:57 +02001353 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001354 """
1355 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001356
Klement Sekeraf62ae122016-10-11 11:47:09 +02001357 :param test:
1358 :param err: error message
1359
1360 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001361 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001362
Damjan Marionf56b77a2016-10-03 19:44:57 +02001363 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001364 """
1365 Get test description
1366
1367 :param test:
1368 :returns: test description
1369
1370 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001371 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001372
Damjan Marionf56b77a2016-10-03 19:44:57 +02001373 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001374 """
1375 Start a test
1376
1377 :param test:
1378
1379 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001380
1381 def print_header(test):
1382 if not hasattr(test.__class__, '_header_printed'):
1383 print(double_line_delim)
1384 print(colorize(getdoc(test).splitlines()[0], GREEN))
1385 print(double_line_delim)
1386 test.__class__._header_printed = True
1387
1388 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001389 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001390 unittest.TestResult.startTest(self, test)
1391 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001392 self.stream.writeln(
1393 "Starting " + self.getDescription(test) + " ...")
1394 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001395
Damjan Marionf56b77a2016-10-03 19:44:57 +02001396 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001397 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001398 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001399
1400 :param test:
1401
1402 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001403 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001404
Damjan Marionf56b77a2016-10-03 19:44:57 +02001405 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001406 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001407 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001408 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001409 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001410 else:
Ole Troan0c629322019-11-28 14:48:44 +01001411 self.stream.writeln("%-68s %4.2f %s" %
1412 (self.getDescription(test),
1413 time.time() - self.start_test,
1414 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001415
1416 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001417
Damjan Marionf56b77a2016-10-03 19:44:57 +02001418 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001419 """
1420 Print errors from running the test case
1421 """
juraj.linkesabec0122018-11-16 17:28:56 +01001422 if len(self.errors) > 0 or len(self.failures) > 0:
1423 self.stream.writeln()
1424 self.printErrorList('ERROR', self.errors)
1425 self.printErrorList('FAIL', self.failures)
1426
1427 # ^^ that is the last output from unittest before summary
1428 if not self.runner.print_summary:
1429 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1430 self.stream = devnull
1431 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001432
Damjan Marionf56b77a2016-10-03 19:44:57 +02001433 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001434 """
1435 Print error list to the output stream together with error type
1436 and test case description.
1437
1438 :param flavour: error type
1439 :param errors: iterable errors
1440
1441 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001442 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001443 self.stream.writeln(double_line_delim)
1444 self.stream.writeln("%s: %s" %
1445 (flavour, self.getDescription(test)))
1446 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001447 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001448
1449
Damjan Marionf56b77a2016-10-03 19:44:57 +02001450class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001451 """
Klement Sekera104543f2017-02-03 07:29:43 +01001452 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001453 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001454
Klement Sekeraf62ae122016-10-11 11:47:09 +02001455 @property
1456 def resultclass(self):
1457 """Class maintaining the results of the tests"""
1458 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001459
juraj.linkes184870a2018-07-16 14:22:01 +02001460 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001461 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001462 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001463 # ignore stream setting here, use hard-coded stdout to be in sync
1464 # with prints from VppTestCase methods ...
1465 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1466 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001467 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001468 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001469
juraj.linkesabec0122018-11-16 17:28:56 +01001470 self.orig_stream = self.stream
1471 self.resultclass.test_framework_result_pipe = result_pipe
1472
1473 self.print_summary = print_summary
1474
1475 def _makeResult(self):
1476 return self.resultclass(self.stream,
1477 self.descriptions,
1478 self.verbosity,
1479 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001480
Damjan Marionf56b77a2016-10-03 19:44:57 +02001481 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001482 """
1483 Run the tests
1484
1485 :param test:
1486
1487 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001488 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001489
1490 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001491 if not self.print_summary:
1492 self.stream = self.orig_stream
1493 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001494 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001495
1496
1497class Worker(Thread):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001498 def __init__(self, args, logger, env=None):
Neale Ranns812ed392017-10-16 04:20:13 -07001499 self.logger = logger
1500 self.args = args
Dave Wallace24564332019-10-21 02:53:14 +00001501 if hasattr(self, 'testcase') and self.testcase.debug_all:
1502 if self.testcase.debug_gdbserver:
1503 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1504 .format(port=self.testcase.gdbserver_port)] + args
1505 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1506 self.args.append(self.wait_for_gdb)
1507 self.app_bin = args[0]
1508 self.app_name = os.path.basename(self.app_bin)
1509 if hasattr(self, 'role'):
1510 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001511 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001512 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001513 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001514 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001515 super(Worker, self).__init__()
1516
Dave Wallace24564332019-10-21 02:53:14 +00001517 def wait_for_enter(self):
1518 if not hasattr(self, 'testcase'):
1519 return
1520 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1521 print()
1522 print(double_line_delim)
1523 print("Spawned GDB Server for '{app}' with PID: {pid}"
1524 .format(app=self.app_name, pid=self.process.pid))
1525 elif self.testcase.debug_all and self.testcase.debug_gdb:
1526 print()
1527 print(double_line_delim)
1528 print("Spawned '{app}' with PID: {pid}"
1529 .format(app=self.app_name, pid=self.process.pid))
1530 else:
1531 return
1532 print(single_line_delim)
1533 print("You can debug '{app}' using:".format(app=self.app_name))
1534 if self.testcase.debug_gdbserver:
1535 print("sudo gdb " + self.app_bin +
1536 " -ex 'target remote localhost:{port}'"
1537 .format(port=self.testcase.gdbserver_port))
1538 print("Now is the time to attach gdb by running the above "
1539 "command, set up breakpoints etc., then resume from "
1540 "within gdb by issuing the 'continue' command")
1541 self.testcase.gdbserver_port += 1
1542 elif self.testcase.debug_gdb:
1543 print("sudo gdb " + self.app_bin +
1544 " -ex 'attach {pid}'".format(pid=self.process.pid))
1545 print("Now is the time to attach gdb by running the above "
1546 "command and set up breakpoints etc., then resume from"
1547 " within gdb by issuing the 'continue' command")
1548 print(single_line_delim)
1549 input("Press ENTER to continue running the testcase...")
1550
Neale Ranns812ed392017-10-16 04:20:13 -07001551 def run(self):
1552 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001553 if not os.path.exists(executable) or not os.access(
1554 executable, os.F_OK | os.X_OK):
1555 # Exit code that means some system file did not exist,
1556 # could not be opened, or had some other kind of error.
1557 self.result = os.EX_OSFILE
1558 raise EnvironmentError(
1559 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001560 self.logger.debug("Running executable: '{app}'"
1561 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001562 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001563 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001564 env["CK_LOG_FILE_NAME"] = "-"
1565 self.process = subprocess.Popen(
1566 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1567 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001568 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001569 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001570 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001571 self.logger.info("Return code is `%s'" % self.process.returncode)
1572 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001573 self.logger.info("Executable `{app}' wrote to stdout:"
1574 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001575 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001576 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001577 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001578 self.logger.info("Executable `{app}' wrote to stderr:"
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(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001582 self.logger.info(single_line_delim)
1583 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001584
Klement Sekera6aa58b72019-05-16 14:34:55 +02001585
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001586if __name__ == '__main__':
1587 pass