blob: e3c605b64ac02e46cfa4a859d2a15c3f9cd3a478 [file] [log] [blame]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001#!/usr/bin/env python
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]:
174 testclass.logger.debug(
175 "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]:
192 testclass.logger.debug(
193 "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 = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100270
Klement Sekeraf62ae122016-10-11 11:47:09 +0200271 @property
272 def packet_infos(self):
273 """List of packet infos"""
274 return self._packet_infos
275
Klement Sekeradab231a2016-12-21 08:50:14 +0100276 @classmethod
277 def get_packet_count_for_if_idx(cls, dst_if_index):
278 """Get the number of packet info for specified destination if index"""
279 if dst_if_index in cls._packet_count_for_dst_if_idx:
280 return cls._packet_count_for_dst_if_idx[dst_if_index]
281 else:
282 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200283
284 @classmethod
285 def instance(cls):
286 """Return the instance of this testcase"""
287 return cls.test_instance
288
Damjan Marionf56b77a2016-10-03 19:44:57 +0200289 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200290 def set_debug_flags(cls, d):
Dave Wallace24564332019-10-21 02:53:14 +0000291 cls.gdbserver_port = 7777
Klement Sekera277b89c2016-10-28 13:20:27 +0200292 cls.debug_core = False
293 cls.debug_gdb = False
294 cls.debug_gdbserver = False
Dave Wallace24564332019-10-21 02:53:14 +0000295 cls.debug_all = False
Klement Sekera277b89c2016-10-28 13:20:27 +0200296 if d is None:
297 return
298 dl = d.lower()
299 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200300 cls.debug_core = True
Dave Wallace24564332019-10-21 02:53:14 +0000301 elif dl == "gdb" or dl == "gdb-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200302 cls.debug_gdb = True
Dave Wallace24564332019-10-21 02:53:14 +0000303 elif dl == "gdbserver" or dl == "gdbserver-all":
Klement Sekera277b89c2016-10-28 13:20:27 +0200304 cls.debug_gdbserver = True
305 else:
306 raise Exception("Unrecognized DEBUG option: '%s'" % d)
Dave Wallace24564332019-10-21 02:53:14 +0000307 if dl == "gdb-all" or dl == "gdbserver-all":
308 cls.debug_all = True
Klement Sekera277b89c2016-10-28 13:20:27 +0200309
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800310 @staticmethod
311 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200312 cpu_usage_list = [set(range(psutil.cpu_count()))]
313 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
314 if 'vpp_main' == p.info['name']]
315 for vpp_process in vpp_processes:
316 for cpu_usage_set in cpu_usage_list:
317 try:
318 cpu_num = vpp_process.cpu_num()
319 if cpu_num in cpu_usage_set:
320 cpu_usage_set_index = cpu_usage_list.index(
321 cpu_usage_set)
322 if cpu_usage_set_index == len(cpu_usage_list) - 1:
323 cpu_usage_list.append({cpu_num})
324 else:
325 cpu_usage_list[cpu_usage_set_index + 1].add(
326 cpu_num)
327 cpu_usage_set.remove(cpu_num)
328 break
329 except psutil.NoSuchProcess:
330 pass
331
332 for cpu_usage_set in cpu_usage_list:
333 if len(cpu_usage_set) > 0:
334 min_usage_set = cpu_usage_set
335 break
336
337 return random.choice(tuple(min_usage_set))
338
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800339 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200340 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200341 """ Set-up the test case class based on environment variables """
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400342 cls.step = BoolEnvironmentVariable('STEP')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100343 d = os.getenv("DEBUG", None)
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400344 # inverted case to handle '' == True
Klement Sekera13a83ef2018-03-21 12:35:51 +0100345 c = os.getenv("CACHE_OUTPUT", "1")
346 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200347 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100348 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
349 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400350 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100351 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
352 plugin_path = None
353 if cls.plugin_path is not None:
354 if cls.extern_plugin_path is not None:
355 plugin_path = "%s:%s" % (
356 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100357 else:
358 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100359 elif cls.extern_plugin_path is not None:
360 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100361 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100362 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100363 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100364 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100365 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100366 if size is not None:
367 coredump_size = "coredump-size %s" % size
368 if coredump_size is None:
369 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200370
Ole Troana45dc072018-12-21 16:04:22 +0100371 cpu_core_number = cls.get_least_used_cpu()
Klement Sekera630ab582019-07-19 09:14:19 +0000372 if not hasattr(cls, "worker_config"):
373 cls.worker_config = ""
juraj.linkes184870a2018-07-16 14:22:01 +0200374
Ole Troana45dc072018-12-21 16:04:22 +0100375 cls.vpp_cmdline = [cls.vpp_bin, "unix",
376 "{", "nodaemon", debug_cli, "full-coredump",
377 coredump_size, "runtime-dir", cls.tempdir, "}",
378 "api-trace", "{", "on", "}", "api-segment", "{",
379 "prefix", cls.shm_prefix, "}", "cpu", "{",
Klement Sekera630ab582019-07-19 09:14:19 +0000380 "main-core", str(cpu_core_number),
381 cls.worker_config, "}",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200382 "statseg", "{", "socket-name", cls.stats_sock, "}",
383 "socksvr", "{", "socket-name", cls.api_sock, "}",
384 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100385 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200386 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100387 "}", "plugin", "unittest_plugin.so", "{", "enable",
388 "}"] + cls.extra_vpp_plugin_config + ["}", ]
389 if cls.extra_vpp_punt_config is not None:
390 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100391 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100392 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400393 if cls.test_plugin_path is not None:
394 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
395
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100396 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
397 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200398
399 @classmethod
400 def wait_for_enter(cls):
401 if cls.debug_gdbserver:
402 print(double_line_delim)
403 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
404 elif cls.debug_gdb:
405 print(double_line_delim)
406 print("Spawned VPP with PID: %d" % cls.vpp.pid)
407 else:
408 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
409 return
410 print(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +0000411 print("You can debug VPP using:")
Klement Sekera277b89c2016-10-28 13:20:27 +0200412 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400413 print("sudo gdb " + cls.vpp_bin +
Dave Wallace24564332019-10-21 02:53:14 +0000414 " -ex 'target remote localhost:{port}'"
415 .format(port=cls.gdbserver_port))
416 print("Now is the time to attach gdb by running the above "
417 "command, set up breakpoints etc., then resume VPP from "
Klement Sekera277b89c2016-10-28 13:20:27 +0200418 "within gdb by issuing the 'continue' command")
Dave Wallace24564332019-10-21 02:53:14 +0000419 cls.gdbserver_port += 1
Klement Sekera277b89c2016-10-28 13:20:27 +0200420 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400421 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Dave Wallace24564332019-10-21 02:53:14 +0000422 print("Now is the time to attach gdb by running the above "
423 "command and set up breakpoints etc., then resume VPP from"
424 " within gdb by issuing the 'continue' command")
Klement Sekera277b89c2016-10-28 13:20:27 +0200425 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800426 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200427
428 @classmethod
429 def run_vpp(cls):
430 cmdline = cls.vpp_cmdline
431
432 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100433 gdbserver = '/usr/bin/gdbserver'
434 if not os.path.isfile(gdbserver) or \
435 not os.access(gdbserver, os.X_OK):
436 raise Exception("gdbserver binary '%s' does not exist or is "
437 "not executable" % gdbserver)
438
Dave Wallace24564332019-10-21 02:53:14 +0000439 cmdline = [gdbserver, 'localhost:{port}'
440 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200441 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
442
Klement Sekera931be3a2016-11-03 05:36:01 +0100443 try:
444 cls.vpp = subprocess.Popen(cmdline,
445 stdout=subprocess.PIPE,
446 stderr=subprocess.PIPE,
447 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800448 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800449 cls.logger.critical("Subprocess returned with non-0 return code: ("
450 "%s)", e.returncode)
451 raise
452 except OSError as e:
453 cls.logger.critical("Subprocess returned with OS error: "
454 "(%s) %s", e.errno, e.strerror)
455 raise
456 except Exception as e:
457 cls.logger.exception("Subprocess returned unexpected from "
458 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100459 raise
460
Klement Sekera277b89c2016-10-28 13:20:27 +0200461 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100462
Damjan Marionf56b77a2016-10-03 19:44:57 +0200463 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200464 def wait_for_stats_socket(cls):
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000465 deadline = time.time() + 300
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800466 ok = False
467 while time.time() < deadline or \
468 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200469 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800470 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200471 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700472 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800473 if not ok:
474 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200475
476 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000477 def wait_for_coredump(cls):
478 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400479 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000480 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400481 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000482 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400483 ok = False
484 while time.time() < deadline:
485 cls.sleep(1)
486 size = curr_size
487 curr_size = os.path.getsize(corefile)
488 if size == curr_size:
489 ok = True
490 break
491 if not ok:
492 cls.logger.error("Timed out waiting for coredump to complete:"
493 " %s", corefile)
494 else:
495 cls.logger.error("Coredump complete: %s, size %d",
496 corefile, curr_size)
497
498 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200499 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200500 """
501 Perform class setup before running the testcase
502 Remove shared memory files, start vpp and connect the vpp-api
503 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800504 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100505 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100506 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100507 cls.logger = get_logger(cls.__name__)
508 if hasattr(cls, 'parallel_handler'):
509 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100510 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700511
Klement Sekeraf62ae122016-10-11 11:47:09 +0200512 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200513 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200514 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200515 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200516 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
517 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100518 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
519 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200520 cls.file_handler.setLevel(DEBUG)
521 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700522 cls.logger.debug("--- setUpClass() for %s called ---" %
523 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200524 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200525 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200526 cls.logger.info("Temporary dir is %s, shm prefix is %s",
527 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200528 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100529 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100530 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200531 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100532 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100533 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200534 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200535 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200536 # need to catch exceptions here because if we raise, then the cleanup
537 # doesn't get called and we might end with a zombie vpp
538 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200539 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200540 cls.reporter.send_keep_alive(cls, 'setUpClass')
541 VppTestResult.current_test_case_info = TestCaseInfo(
542 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100543 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100544 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100545 cls.pump_thread_stop_flag = Event()
546 cls.pump_thread_wakeup_pipe = os.pipe()
547 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100548 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100549 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200550 if cls.debug_gdb or cls.debug_gdbserver:
551 read_timeout = 0
552 else:
553 read_timeout = 5
554 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
555 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100556 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400557 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100558 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400559 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100560 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200561 cls.wait_for_stats_socket()
562 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200563 try:
564 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100565 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200566 cls.vpp_startup_failed = True
567 cls.logger.critical(
568 "VPP died shortly after startup, check the"
569 " output to standard error for possible cause")
570 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100571 try:
572 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100573 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100574 try:
575 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100576 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100577 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100578 if cls.debug_gdbserver:
579 print(colorize("You're running VPP inside gdbserver but "
580 "VPP-API connection failed, did you forget "
581 "to 'continue' VPP from within gdb?", RED))
582 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000583 except Exception as e:
584 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400585
586 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100587 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200588
Damjan Marionf56b77a2016-10-03 19:44:57 +0200589 @classmethod
590 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200591 """
592 Disconnect vpp-api, kill vpp and cleanup shared memory files
593 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200594 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
595 cls.vpp.poll()
596 if cls.vpp.returncode is None:
Dave Wallace24564332019-10-21 02:53:14 +0000597 print()
Klement Sekera277b89c2016-10-28 13:20:27 +0200598 print(double_line_delim)
599 print("VPP or GDB server is still running")
600 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800601 input("When done debugging, press ENTER to kill the "
602 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200603
juraj.linkes184870a2018-07-16 14:22:01 +0200604 # first signal that we want to stop the pump thread, then wake it up
605 if hasattr(cls, 'pump_thread_stop_flag'):
606 cls.pump_thread_stop_flag.set()
607 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100608 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100609 if hasattr(cls, 'pump_thread'):
610 cls.logger.debug("Waiting for pump thread to stop")
611 cls.pump_thread.join()
612 if hasattr(cls, 'vpp_stderr_reader_thread'):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000613 cls.logger.debug("Waiting for stdderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100614 cls.vpp_stderr_reader_thread.join()
615
Klement Sekeraf62ae122016-10-11 11:47:09 +0200616 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100617 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700618 cls.logger.debug("Disconnecting class vapi client on %s",
619 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100620 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700621 cls.logger.debug("Deleting class vapi attribute on %s",
622 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100623 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200624 cls.vpp.poll()
625 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000626 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100627 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400628 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100629 cls.logger.debug("Waiting for vpp to die")
630 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700631 cls.logger.debug("Deleting class vpp attribute on %s",
632 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200633 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200634
Klement Sekera3747c752017-04-10 06:30:17 +0200635 if cls.vpp_startup_failed:
636 stdout_log = cls.logger.info
637 stderr_log = cls.logger.critical
638 else:
639 stdout_log = cls.logger.info
640 stderr_log = cls.logger.info
641
Klement Sekerae4504c62016-12-08 10:16:41 +0100642 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200643 stdout_log(single_line_delim)
644 stdout_log('VPP output to stdout while running %s:', cls.__name__)
645 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100646 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200647 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
648 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200649 stdout_log('\n%s', vpp_output)
650 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200651
Klement Sekerae4504c62016-12-08 10:16:41 +0100652 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200653 stderr_log(single_line_delim)
654 stderr_log('VPP output to stderr while running %s:', cls.__name__)
655 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100656 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200657 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
658 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200659 stderr_log('\n%s', vpp_output)
660 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200661
Damjan Marionf56b77a2016-10-03 19:44:57 +0200662 @classmethod
663 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200664 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700665 cls.logger.debug("--- tearDownClass() for %s called ---" %
666 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200667 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200668 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200669 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100670 cls.reset_packet_infos()
671 if debug_framework:
672 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200673
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700674 def show_commands_at_teardown(self):
675 """ Allow subclass specific teardown logging additions."""
676 self.logger.info("--- No test specific show commands provided. ---")
677
Damjan Marionf56b77a2016-10-03 19:44:57 +0200678 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200679 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100680 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
681 (self.__class__.__name__, self._testMethodName,
682 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700683
684 try:
685 if not self.vpp_dead:
686 self.logger.debug(self.vapi.cli("show trace max 1000"))
687 self.logger.info(self.vapi.ppcli("show interface"))
688 self.logger.info(self.vapi.ppcli("show hardware"))
689 self.logger.info(self.statistics.set_errors_str())
690 self.logger.info(self.vapi.ppcli("show run"))
691 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400692 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700693 self.logger.info("Logging testcase specific show commands.")
694 self.show_commands_at_teardown()
695 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500696 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000697 m = self._testMethodName
698 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500699 tmp_api_trace = "/tmp/%s" % api_trace
700 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
701 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
702 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
703 vpp_api_trace_log))
704 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500705 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500706 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700707 except VppTransportShmemIOError:
708 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
709 "Cannot log show commands.")
710 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100711 else:
712 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200713
Damjan Marionf56b77a2016-10-03 19:44:57 +0200714 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200715 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800716 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200717 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100718 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400719
720 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
721 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100722 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100723 self.vpp_stdout_deque.append(
724 "--- test setUp() for %s.%s(%s) starts here ---\n" %
725 (self.__class__.__name__, self._testMethodName,
726 self._testMethodDoc))
727 self.vpp_stderr_deque.append(
728 "--- test setUp() for %s.%s(%s) starts here ---\n" %
729 (self.__class__.__name__, self._testMethodName,
730 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200731 self.vapi.cli("clear trace")
732 # store the test instance inside the test class - so that objects
733 # holding the class can access instance methods (like assertEqual)
734 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200735
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200737 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200738 """
739 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200740
Klement Sekera75e7d132017-09-20 08:26:30 +0200741 :param interfaces: iterable interface indexes (if None,
742 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200743
Klement Sekeraf62ae122016-10-11 11:47:09 +0200744 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200745 if interfaces is None:
746 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200747 for i in interfaces:
748 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200749
Damjan Marionf56b77a2016-10-03 19:44:57 +0200750 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100751 def register_capture(cls, cap_name):
752 """ Register a capture in the testclass """
753 # add to the list of captures with current timestamp
754 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100755
756 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000757 def get_vpp_time(cls):
758 return float(cls.vapi.cli('show clock').replace("Time now ", ""))
759
760 @classmethod
761 def sleep_on_vpp_time(cls, sec):
762 """ Sleep according to time in VPP world """
763 # On a busy system with many processes
764 # we might end up with VPP time being slower than real world
765 # So take that into account when waiting for VPP to do something
766 start_time = cls.get_vpp_time()
767 while cls.get_vpp_time() - start_time < sec:
768 cls.sleep(0.1)
769
770 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100771 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000772 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100773 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200774 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000775 # PG, when starts, runs to completion -
776 # so let's avoid a race condition,
777 # and wait a little till it's done.
778 # Then clean it up - and then be gone.
779 deadline = time.time() + 300
780 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
781 cls.sleep(0.01) # yield
782 if time.time() > deadline:
783 cls.logger.error("Timeout waiting for pg to stop")
784 break
785 for stamp, cap_name in cls._captures:
786 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100787 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200788
Damjan Marionf56b77a2016-10-03 19:44:57 +0200789 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200790 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200791 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100792 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200793
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100794 :param interfaces: iterable indexes of the interfaces.
795 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200796
Klement Sekeraf62ae122016-10-11 11:47:09 +0200797 """
798 result = []
799 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200800 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200801 setattr(cls, intf.name, intf)
802 result.append(intf)
803 cls.pg_interfaces = result
804 return result
805
Matej Klotton0178d522016-11-04 11:11:44 +0100806 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200807 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100808 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100809 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100810
Klement Sekerab9ef2732018-06-24 22:49:33 +0200811 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100812 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100813 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200814 result = [VppLoInterface(cls) for i in range(count)]
815 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100816 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100817 cls.lo_interfaces = result
818 return result
819
Neale Ranns192b13f2019-03-15 02:16:20 -0700820 @classmethod
821 def create_bvi_interfaces(cls, count):
822 """
823 Create BVI interfaces.
824
825 :param count: number of interfaces created.
826 :returns: List of created interfaces.
827 """
828 result = [VppBviInterface(cls) for i in range(count)]
829 for intf in result:
830 setattr(cls, intf.name, intf)
831 cls.bvi_interfaces = result
832 return result
833
Damjan Marionf56b77a2016-10-03 19:44:57 +0200834 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200835 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200836 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200837 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200838 NOTE: Currently works only when Raw layer is present.
839
840 :param packet: packet
841 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200842 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200843
844 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200845 packet_len = len(packet) + 4
846 extend = size - packet_len
847 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200848 num = (extend // len(padding)) + 1
849 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200850
Klement Sekeradab231a2016-12-21 08:50:14 +0100851 @classmethod
852 def reset_packet_infos(cls):
853 """ Reset the list of packet info objects and packet counts to zero """
854 cls._packet_infos = {}
855 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200856
Klement Sekeradab231a2016-12-21 08:50:14 +0100857 @classmethod
858 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200859 """
860 Create packet info object containing the source and destination indexes
861 and add it to the testcase's packet info list
862
Klement Sekeradab231a2016-12-21 08:50:14 +0100863 :param VppInterface src_if: source interface
864 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200865
866 :returns: _PacketInfo object
867
868 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200869 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100870 info.index = len(cls._packet_infos)
871 info.src = src_if.sw_if_index
872 info.dst = dst_if.sw_if_index
873 if isinstance(dst_if, VppSubInterface):
874 dst_idx = dst_if.parent.sw_if_index
875 else:
876 dst_idx = dst_if.sw_if_index
877 if dst_idx in cls._packet_count_for_dst_if_idx:
878 cls._packet_count_for_dst_if_idx[dst_idx] += 1
879 else:
880 cls._packet_count_for_dst_if_idx[dst_idx] = 1
881 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200882 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200883
Damjan Marionf56b77a2016-10-03 19:44:57 +0200884 @staticmethod
885 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200886 """
887 Convert _PacketInfo object to packet payload
888
889 :param info: _PacketInfo object
890
891 :returns: string containing serialized data from packet info
892 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100893 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
894 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200895
Damjan Marionf56b77a2016-10-03 19:44:57 +0200896 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800897 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200898 """
899 Convert packet payload to _PacketInfo object
900
901 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700902 :type payload: <class 'scapy.packet.Raw'>
903 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800904 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700905 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200906 :returns: _PacketInfo object containing de-serialized data from payload
907
908 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800909 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200910 info = _PacketInfo()
911 info.index = int(numbers[0])
912 info.src = int(numbers[1])
913 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100914 info.ip = int(numbers[3])
915 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200916 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200917
Damjan Marionf56b77a2016-10-03 19:44:57 +0200918 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200919 """
920 Iterate over the packet info list stored in the testcase
921 Start iteration with first element if info is None
922 Continue based on index in info if info is specified
923
924 :param info: info or None
925 :returns: next info in list or None if no more infos
926 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200927 if info is None:
928 next_index = 0
929 else:
930 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100931 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200932 return None
933 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100934 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200935
Klement Sekeraf62ae122016-10-11 11:47:09 +0200936 def get_next_packet_info_for_interface(self, src_index, info):
937 """
938 Search the packet info list for the next packet info with same source
939 interface index
940
941 :param src_index: source interface index to search for
942 :param info: packet info - where to start the search
943 :returns: packet info or None
944
945 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200946 while True:
947 info = self.get_next_packet_info(info)
948 if info is None:
949 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200950 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200951 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200952
Klement Sekeraf62ae122016-10-11 11:47:09 +0200953 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
954 """
955 Search the packet info list for the next packet info with same source
956 and destination interface indexes
957
958 :param src_index: source interface index to search for
959 :param dst_index: destination interface index to search for
960 :param info: packet info - where to start the search
961 :returns: packet info or None
962
963 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200964 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200965 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200966 if info is None:
967 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200968 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200969 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200970
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200971 def assert_equal(self, real_value, expected_value, name_or_class=None):
972 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100973 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200974 return
975 try:
976 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
977 msg = msg % (getdoc(name_or_class).strip(),
978 real_value, str(name_or_class(real_value)),
979 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100980 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200981 msg = "Invalid %s: %s does not match expected value %s" % (
982 name_or_class, real_value, expected_value)
983
984 self.assertEqual(real_value, expected_value, msg)
985
Klement Sekerab17dd962017-01-09 07:43:48 +0100986 def assert_in_range(self,
987 real_value,
988 expected_min,
989 expected_max,
990 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200991 if name is None:
992 msg = None
993 else:
994 msg = "Invalid %s: %s out of range <%s,%s>" % (
995 name, real_value, expected_min, expected_max)
996 self.assertTrue(expected_min <= real_value <= expected_max, msg)
997
Klement Sekerad81ae412018-05-16 10:52:54 +0200998 def assert_packet_checksums_valid(self, packet,
999 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001000 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001001 udp_layers = ['UDP', 'UDPerror']
1002 checksum_fields = ['cksum', 'chksum']
1003 checksums = []
1004 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001005 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001006 while True:
1007 layer = temp.getlayer(counter)
1008 if layer:
1009 for cf in checksum_fields:
1010 if hasattr(layer, cf):
1011 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001012 0 == getattr(layer, cf) and \
1013 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001014 continue
1015 delattr(layer, cf)
1016 checksums.append((counter, cf))
1017 else:
1018 break
1019 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001020 if 0 == len(checksums):
1021 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001022 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001023 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001024 calc_sum = getattr(temp[layer], cf)
1025 self.assert_equal(
1026 getattr(received[layer], cf), calc_sum,
1027 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1028 self.logger.debug(
1029 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1030 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001031
1032 def assert_checksum_valid(self, received_packet, layer,
1033 field_name='chksum',
1034 ignore_zero_checksum=False):
1035 """ Check checksum of received packet on given layer """
1036 received_packet_checksum = getattr(received_packet[layer], field_name)
1037 if ignore_zero_checksum and 0 == received_packet_checksum:
1038 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001039 recalculated = received_packet.__class__(
1040 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001041 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001042 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001043 self.assert_equal(received_packet_checksum,
1044 getattr(recalculated[layer], field_name),
1045 "packet checksum on layer: %s" % layer)
1046
1047 def assert_ip_checksum_valid(self, received_packet,
1048 ignore_zero_checksum=False):
1049 self.assert_checksum_valid(received_packet, 'IP',
1050 ignore_zero_checksum=ignore_zero_checksum)
1051
1052 def assert_tcp_checksum_valid(self, received_packet,
1053 ignore_zero_checksum=False):
1054 self.assert_checksum_valid(received_packet, 'TCP',
1055 ignore_zero_checksum=ignore_zero_checksum)
1056
1057 def assert_udp_checksum_valid(self, received_packet,
1058 ignore_zero_checksum=True):
1059 self.assert_checksum_valid(received_packet, 'UDP',
1060 ignore_zero_checksum=ignore_zero_checksum)
1061
1062 def assert_embedded_icmp_checksum_valid(self, received_packet):
1063 if received_packet.haslayer(IPerror):
1064 self.assert_checksum_valid(received_packet, 'IPerror')
1065 if received_packet.haslayer(TCPerror):
1066 self.assert_checksum_valid(received_packet, 'TCPerror')
1067 if received_packet.haslayer(UDPerror):
1068 self.assert_checksum_valid(received_packet, 'UDPerror',
1069 ignore_zero_checksum=True)
1070 if received_packet.haslayer(ICMPerror):
1071 self.assert_checksum_valid(received_packet, 'ICMPerror')
1072
1073 def assert_icmp_checksum_valid(self, received_packet):
1074 self.assert_checksum_valid(received_packet, 'ICMP')
1075 self.assert_embedded_icmp_checksum_valid(received_packet)
1076
1077 def assert_icmpv6_checksum_valid(self, pkt):
1078 if pkt.haslayer(ICMPv6DestUnreach):
1079 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1080 self.assert_embedded_icmp_checksum_valid(pkt)
1081 if pkt.haslayer(ICMPv6EchoRequest):
1082 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1083 if pkt.haslayer(ICMPv6EchoReply):
1084 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1085
Klement Sekera3a343d42019-05-16 14:35:46 +02001086 def get_packet_counter(self, counter):
1087 if counter.startswith("/"):
1088 counter_value = self.statistics.get_counter(counter)
1089 else:
1090 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001091 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001092 for i in range(1, len(counters) - 1):
1093 results = counters[i].split()
1094 if results[1] == counter:
1095 counter_value = int(results[0])
1096 break
1097 return counter_value
1098
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001099 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001100 counter_value = self.get_packet_counter(counter)
1101 self.assert_equal(counter_value, expected_value,
1102 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001103
Ole Troan233e4682019-05-16 15:01:34 +02001104 def assert_error_counter_equal(self, counter, expected_value):
1105 counter_value = self.statistics.get_err_counter(counter)
1106 self.assert_equal(counter_value, expected_value,
1107 "error counter `%s'" % counter)
1108
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001109 @classmethod
1110 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001111
1112 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1113 # * by Guido, only the main thread can be interrupted.
1114 # */
1115 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1116 if timeout == 0:
1117 # yield quantum
1118 if hasattr(os, 'sched_yield'):
1119 os.sched_yield()
1120 else:
1121 time.sleep(0)
1122 return
1123
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001124 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001125 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001126 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001127 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001128 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001129 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001130 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001131 "slept for %es instead of ~%es!",
1132 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001133 if hasattr(cls, 'logger'):
1134 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001135 "Finished sleep (%s) - slept %es (wanted %es)",
1136 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001137
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001138 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001139 self.vapi.cli("clear trace")
1140 intf.add_stream(pkts)
1141 self.pg_enable_capture(self.pg_interfaces)
1142 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001143
1144 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1145 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001146 if not timeout:
1147 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001148 for i in self.pg_interfaces:
1149 i.get_capture(0, timeout=timeout)
1150 i.assert_nothing_captured(remark=remark)
1151 timeout = 0.1
1152
Neale Rannsd7603d92019-03-28 08:56:10 +00001153 def send_and_expect(self, intf, pkts, output, n_rx=None):
1154 if not n_rx:
1155 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001156 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001157 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001158 return rx
1159
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001160 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1161 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001162 rx = output.get_capture(len(pkts))
1163 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001164 if not timeout:
1165 timeout = 1
1166 for i in self.pg_interfaces:
1167 if i not in outputs:
1168 i.get_capture(0, timeout=timeout)
1169 i.assert_nothing_captured()
1170 timeout = 0.1
1171
Neale Ranns52fae862018-01-08 04:41:42 -08001172 return rx
1173
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001174 def runTest(self):
1175 """ unittest calls runTest when TestCase is instantiated without a
1176 test case. Use case: Writing unittests against VppTestCase"""
1177 pass
1178
Damjan Marionf56b77a2016-10-03 19:44:57 +02001179
juraj.linkes184870a2018-07-16 14:22:01 +02001180def get_testcase_doc_name(test):
1181 return getdoc(test.__class__).splitlines()[0]
1182
1183
Ole Trøan5ba91592018-11-22 10:01:09 +00001184def get_test_description(descriptions, test):
1185 short_description = test.shortDescription()
1186 if descriptions and short_description:
1187 return short_description
1188 else:
1189 return str(test)
1190
1191
juraj.linkes40dd73b2018-09-21 13:55:16 +02001192class TestCaseInfo(object):
1193 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1194 self.logger = logger
1195 self.tempdir = tempdir
1196 self.vpp_pid = vpp_pid
1197 self.vpp_bin_path = vpp_bin_path
1198 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001199
1200
Damjan Marionf56b77a2016-10-03 19:44:57 +02001201class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001202 """
1203 @property result_string
1204 String variable to store the test case result string.
1205 @property errors
1206 List variable containing 2-tuples of TestCase instances and strings
1207 holding formatted tracebacks. Each tuple represents a test which
1208 raised an unexpected exception.
1209 @property failures
1210 List variable containing 2-tuples of TestCase instances and strings
1211 holding formatted tracebacks. Each tuple represents a test where
1212 a failure was explicitly signalled using the TestCase.assert*()
1213 methods.
1214 """
1215
juraj.linkes40dd73b2018-09-21 13:55:16 +02001216 failed_test_cases_info = set()
1217 core_crash_test_cases_info = set()
1218 current_test_case_info = None
1219
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001220 def __init__(self, stream=None, descriptions=None, verbosity=None,
1221 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001222 """
Klement Sekerada505f62017-01-04 12:58:53 +01001223 :param stream File descriptor to store where to report test results.
1224 Set to the standard error stream by default.
1225 :param descriptions Boolean variable to store information if to use
1226 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001227 :param verbosity Integer variable to store required verbosity level.
1228 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001229 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001230 self.stream = stream
1231 self.descriptions = descriptions
1232 self.verbosity = verbosity
1233 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001234 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001235
Damjan Marionf56b77a2016-10-03 19:44:57 +02001236 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001237 """
1238 Record a test succeeded result
1239
1240 :param test:
1241
1242 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001243 if self.current_test_case_info:
1244 self.current_test_case_info.logger.debug(
1245 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1246 test._testMethodName,
1247 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001248 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001249 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001250
juraj.linkescae64f82018-09-19 15:01:47 +02001251 self.send_result_through_pipe(test, PASS)
1252
Klement Sekeraf62ae122016-10-11 11:47:09 +02001253 def addSkip(self, test, reason):
1254 """
1255 Record a test skipped.
1256
1257 :param test:
1258 :param reason:
1259
1260 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001261 if self.current_test_case_info:
1262 self.current_test_case_info.logger.debug(
1263 "--- addSkip() %s.%s(%s) called, reason is %s" %
1264 (test.__class__.__name__, test._testMethodName,
1265 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001266 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001267 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001268
juraj.linkescae64f82018-09-19 15:01:47 +02001269 self.send_result_through_pipe(test, SKIP)
1270
juraj.linkes40dd73b2018-09-21 13:55:16 +02001271 def symlink_failed(self):
1272 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001273 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001274 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001275 link_path = os.path.join(
1276 failed_dir,
1277 '%s-FAILED' %
1278 os.path.basename(self.current_test_case_info.tempdir))
1279 if self.current_test_case_info.logger:
1280 self.current_test_case_info.logger.debug(
1281 "creating a link to the failed test")
1282 self.current_test_case_info.logger.debug(
1283 "os.symlink(%s, %s)" %
1284 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001285 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001286 if self.current_test_case_info.logger:
1287 self.current_test_case_info.logger.debug(
1288 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001289 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001290 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001291
Klement Sekeraf413bef2017-08-15 07:09:02 +02001292 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001293 if self.current_test_case_info.logger:
1294 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001295
juraj.linkescae64f82018-09-19 15:01:47 +02001296 def send_result_through_pipe(self, test, result):
1297 if hasattr(self, 'test_framework_result_pipe'):
1298 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001299 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001300 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001301
juraj.linkes40dd73b2018-09-21 13:55:16 +02001302 def log_error(self, test, err, fn_name):
1303 if self.current_test_case_info:
1304 if isinstance(test, unittest.suite._ErrorHolder):
1305 test_name = test.description
1306 else:
1307 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1308 test._testMethodName,
1309 test._testMethodDoc)
1310 self.current_test_case_info.logger.debug(
1311 "--- %s() %s called, err is %s" %
1312 (fn_name, test_name, err))
1313 self.current_test_case_info.logger.debug(
1314 "formatted exception is:\n%s" %
1315 "".join(format_exception(*err)))
1316
1317 def add_error(self, test, err, unittest_fn, error_type):
1318 if error_type == FAIL:
1319 self.log_error(test, err, 'addFailure')
1320 error_type_str = colorize("FAIL", RED)
1321 elif error_type == ERROR:
1322 self.log_error(test, err, 'addError')
1323 error_type_str = colorize("ERROR", RED)
1324 else:
1325 raise Exception('Error type %s cannot be used to record an '
1326 'error or a failure' % error_type)
1327
1328 unittest_fn(self, test, err)
1329 if self.current_test_case_info:
1330 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1331 (error_type_str,
1332 self.current_test_case_info.tempdir)
1333 self.symlink_failed()
1334 self.failed_test_cases_info.add(self.current_test_case_info)
1335 if is_core_present(self.current_test_case_info.tempdir):
1336 if not self.current_test_case_info.core_crash_test:
1337 if isinstance(test, unittest.suite._ErrorHolder):
1338 test_name = str(test)
1339 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001340 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001341 get_testcase_doc_name(test), test.id())
1342 self.current_test_case_info.core_crash_test = test_name
1343 self.core_crash_test_cases_info.add(
1344 self.current_test_case_info)
1345 else:
1346 self.result_string = '%s [no temp dir]' % error_type_str
1347
1348 self.send_result_through_pipe(test, error_type)
1349
Damjan Marionf56b77a2016-10-03 19:44:57 +02001350 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001351 """
1352 Record a test failed result
1353
1354 :param test:
1355 :param err: error message
1356
1357 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001358 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001359
Damjan Marionf56b77a2016-10-03 19:44:57 +02001360 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001361 """
1362 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001363
Klement Sekeraf62ae122016-10-11 11:47:09 +02001364 :param test:
1365 :param err: error message
1366
1367 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001368 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001369
Damjan Marionf56b77a2016-10-03 19:44:57 +02001370 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001371 """
1372 Get test description
1373
1374 :param test:
1375 :returns: test description
1376
1377 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001378 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001379
Damjan Marionf56b77a2016-10-03 19:44:57 +02001380 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001381 """
1382 Start a test
1383
1384 :param test:
1385
1386 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001387
1388 def print_header(test):
1389 if not hasattr(test.__class__, '_header_printed'):
1390 print(double_line_delim)
1391 print(colorize(getdoc(test).splitlines()[0], GREEN))
1392 print(double_line_delim)
1393 test.__class__._header_printed = True
1394
1395 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001396
Damjan Marionf56b77a2016-10-03 19:44:57 +02001397 unittest.TestResult.startTest(self, test)
1398 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001399 self.stream.writeln(
1400 "Starting " + self.getDescription(test) + " ...")
1401 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001402
Damjan Marionf56b77a2016-10-03 19:44:57 +02001403 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001404 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001405 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001406
1407 :param test:
1408
1409 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001410 unittest.TestResult.stopTest(self, test)
1411 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001412 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001413 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001414 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001415 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001416 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001417 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001418 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001419
1420 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001421
Damjan Marionf56b77a2016-10-03 19:44:57 +02001422 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001423 """
1424 Print errors from running the test case
1425 """
juraj.linkesabec0122018-11-16 17:28:56 +01001426 if len(self.errors) > 0 or len(self.failures) > 0:
1427 self.stream.writeln()
1428 self.printErrorList('ERROR', self.errors)
1429 self.printErrorList('FAIL', self.failures)
1430
1431 # ^^ that is the last output from unittest before summary
1432 if not self.runner.print_summary:
1433 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1434 self.stream = devnull
1435 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001436
Damjan Marionf56b77a2016-10-03 19:44:57 +02001437 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001438 """
1439 Print error list to the output stream together with error type
1440 and test case description.
1441
1442 :param flavour: error type
1443 :param errors: iterable errors
1444
1445 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001446 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001447 self.stream.writeln(double_line_delim)
1448 self.stream.writeln("%s: %s" %
1449 (flavour, self.getDescription(test)))
1450 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001451 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001452
1453
Damjan Marionf56b77a2016-10-03 19:44:57 +02001454class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001455 """
Klement Sekera104543f2017-02-03 07:29:43 +01001456 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001457 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001458
Klement Sekeraf62ae122016-10-11 11:47:09 +02001459 @property
1460 def resultclass(self):
1461 """Class maintaining the results of the tests"""
1462 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001463
juraj.linkes184870a2018-07-16 14:22:01 +02001464 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001465 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001466 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001467 # ignore stream setting here, use hard-coded stdout to be in sync
1468 # with prints from VppTestCase methods ...
1469 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1470 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001471 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001472 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001473
juraj.linkesabec0122018-11-16 17:28:56 +01001474 self.orig_stream = self.stream
1475 self.resultclass.test_framework_result_pipe = result_pipe
1476
1477 self.print_summary = print_summary
1478
1479 def _makeResult(self):
1480 return self.resultclass(self.stream,
1481 self.descriptions,
1482 self.verbosity,
1483 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001484
Damjan Marionf56b77a2016-10-03 19:44:57 +02001485 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001486 """
1487 Run the tests
1488
1489 :param test:
1490
1491 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001492 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001493
1494 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001495 if not self.print_summary:
1496 self.stream = self.orig_stream
1497 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001498 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001499
1500
1501class Worker(Thread):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001502 def __init__(self, args, logger, env=None):
Neale Ranns812ed392017-10-16 04:20:13 -07001503 self.logger = logger
1504 self.args = args
Dave Wallace24564332019-10-21 02:53:14 +00001505 if hasattr(self, 'testcase') and self.testcase.debug_all:
1506 if self.testcase.debug_gdbserver:
1507 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1508 .format(port=self.testcase.gdbserver_port)] + args
1509 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1510 self.args.append(self.wait_for_gdb)
1511 self.app_bin = args[0]
1512 self.app_name = os.path.basename(self.app_bin)
1513 if hasattr(self, 'role'):
1514 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001515 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001516 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001517 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001518 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001519 super(Worker, self).__init__()
1520
Dave Wallace24564332019-10-21 02:53:14 +00001521 def wait_for_enter(self):
1522 if not hasattr(self, 'testcase'):
1523 return
1524 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1525 print()
1526 print(double_line_delim)
1527 print("Spawned GDB Server for '{app}' with PID: {pid}"
1528 .format(app=self.app_name, pid=self.process.pid))
1529 elif self.testcase.debug_all and self.testcase.debug_gdb:
1530 print()
1531 print(double_line_delim)
1532 print("Spawned '{app}' with PID: {pid}"
1533 .format(app=self.app_name, pid=self.process.pid))
1534 else:
1535 return
1536 print(single_line_delim)
1537 print("You can debug '{app}' using:".format(app=self.app_name))
1538 if self.testcase.debug_gdbserver:
1539 print("sudo gdb " + self.app_bin +
1540 " -ex 'target remote localhost:{port}'"
1541 .format(port=self.testcase.gdbserver_port))
1542 print("Now is the time to attach gdb by running the above "
1543 "command, set up breakpoints etc., then resume from "
1544 "within gdb by issuing the 'continue' command")
1545 self.testcase.gdbserver_port += 1
1546 elif self.testcase.debug_gdb:
1547 print("sudo gdb " + self.app_bin +
1548 " -ex 'attach {pid}'".format(pid=self.process.pid))
1549 print("Now is the time to attach gdb by running the above "
1550 "command and set up breakpoints etc., then resume from"
1551 " within gdb by issuing the 'continue' command")
1552 print(single_line_delim)
1553 input("Press ENTER to continue running the testcase...")
1554
Neale Ranns812ed392017-10-16 04:20:13 -07001555 def run(self):
1556 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001557 if not os.path.exists(executable) or not os.access(
1558 executable, os.F_OK | os.X_OK):
1559 # Exit code that means some system file did not exist,
1560 # could not be opened, or had some other kind of error.
1561 self.result = os.EX_OSFILE
1562 raise EnvironmentError(
1563 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001564 self.logger.debug("Running executable: '{app}'"
1565 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001566 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001567 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001568 env["CK_LOG_FILE_NAME"] = "-"
1569 self.process = subprocess.Popen(
1570 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1571 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001572 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001573 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001574 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001575 self.logger.info("Return code is `%s'" % self.process.returncode)
1576 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001577 self.logger.info("Executable `{app}' wrote to stdout:"
1578 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001579 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001580 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001581 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001582 self.logger.info("Executable `{app}' wrote to stderr:"
1583 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001584 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001585 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001586 self.logger.info(single_line_delim)
1587 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001588
Klement Sekera6aa58b72019-05-16 14:34:55 +02001589
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001590if __name__ == '__main__':
1591 pass