blob: eb59aadd8bf3ffcdc0886001f6c546b70e85f667 [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
Klement Sekera611864f2018-09-26 11:19:00 +0200466 def wait_for_stats_socket(cls):
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000467 deadline = time.time() + 300
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800468 ok = False
469 while time.time() < deadline or \
470 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200471 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800472 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200473 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700474 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800475 if not ok:
476 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200477
478 @classmethod
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000479 def wait_for_coredump(cls):
480 corefile = cls.tempdir + "/core"
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400481 if os.path.isfile(corefile):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000482 cls.logger.error("Waiting for coredump to complete: %s", corefile)
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400483 curr_size = os.path.getsize(corefile)
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000484 deadline = time.time() + 60
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400485 ok = False
486 while time.time() < deadline:
487 cls.sleep(1)
488 size = curr_size
489 curr_size = os.path.getsize(corefile)
490 if size == curr_size:
491 ok = True
492 break
493 if not ok:
494 cls.logger.error("Timed out waiting for coredump to complete:"
495 " %s", corefile)
496 else:
497 cls.logger.error("Coredump complete: %s, size %d",
498 corefile, curr_size)
499
500 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200501 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200502 """
503 Perform class setup before running the testcase
504 Remove shared memory files, start vpp and connect the vpp-api
505 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800506 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100507 gc.collect() # run garbage collection first
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100508 cls.logger = get_logger(cls.__name__)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000509 seed = os.environ["RND_SEED"]
510 random.seed(seed)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100511 if hasattr(cls, 'parallel_handler'):
512 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100513 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700514
Klement Sekeraf62ae122016-10-11 11:47:09 +0200515 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200516 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200517 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200518 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200519 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
520 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100521 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
522 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200523 cls.file_handler.setLevel(DEBUG)
524 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700525 cls.logger.debug("--- setUpClass() for %s called ---" %
526 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200527 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200528 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200529 cls.logger.info("Temporary dir is %s, shm prefix is %s",
530 cls.tempdir, cls.shm_prefix)
Klement Sekera45a95dd2019-11-05 11:18:25 +0000531 cls.logger.debug("Random seed is %s" % seed)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200532 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100533 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100534 cls._captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200535 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100536 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100537 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200538 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200539 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200540 # need to catch exceptions here because if we raise, then the cleanup
541 # doesn't get called and we might end with a zombie vpp
542 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200543 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200544 cls.reporter.send_keep_alive(cls, 'setUpClass')
545 VppTestResult.current_test_case_info = TestCaseInfo(
546 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100547 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100548 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100549 cls.pump_thread_stop_flag = Event()
550 cls.pump_thread_wakeup_pipe = os.pipe()
551 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100552 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100553 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200554 if cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400555 cls.vapi_response_timeout = 0
Klement Sekera611864f2018-09-26 11:19:00 +0200556 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
Paul Vinciguerrabfd7d292019-10-26 22:25:49 -0400557 cls.vapi_response_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100558 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400559 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100560 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400561 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100562 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200563 cls.wait_for_stats_socket()
564 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200565 try:
566 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100567 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200568 cls.vpp_startup_failed = True
569 cls.logger.critical(
570 "VPP died shortly after startup, check the"
571 " output to standard error for possible cause")
572 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100573 try:
574 cls.vapi.connect()
Paul Vinciguerra1043fd32019-12-02 21:42:28 -0500575 except vpp_papi.VPPIOError as e:
576 cls.logger.debug("Exception connecting to vapi: %s" % e)
577 cls.vapi.disconnect()
578
Klement Sekera085f5c02016-11-24 01:59:16 +0100579 if cls.debug_gdbserver:
580 print(colorize("You're running VPP inside gdbserver but "
581 "VPP-API connection failed, did you forget "
582 "to 'continue' VPP from within gdb?", RED))
583 raise
Andrew Yourtchenko4f05a8e2019-10-13 10:06:46 +0000584 except Exception as e:
585 cls.logger.debug("Exception connecting to VPP: %s" % e)
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400586
587 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100588 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200589
Damjan Marionf56b77a2016-10-03 19:44:57 +0200590 @classmethod
591 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200592 """
593 Disconnect vpp-api, kill vpp and cleanup shared memory files
594 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200595 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
596 cls.vpp.poll()
597 if cls.vpp.returncode is None:
Dave Wallace24564332019-10-21 02:53:14 +0000598 print()
Klement Sekera277b89c2016-10-28 13:20:27 +0200599 print(double_line_delim)
600 print("VPP or GDB server is still running")
601 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800602 input("When done debugging, press ENTER to kill the "
603 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200604
juraj.linkes184870a2018-07-16 14:22:01 +0200605 # first signal that we want to stop the pump thread, then wake it up
606 if hasattr(cls, 'pump_thread_stop_flag'):
607 cls.pump_thread_stop_flag.set()
608 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100609 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100610 if hasattr(cls, 'pump_thread'):
611 cls.logger.debug("Waiting for pump thread to stop")
612 cls.pump_thread.join()
613 if hasattr(cls, 'vpp_stderr_reader_thread'):
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000614 cls.logger.debug("Waiting for stdderr pump to stop")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100615 cls.vpp_stderr_reader_thread.join()
616
Klement Sekeraf62ae122016-10-11 11:47:09 +0200617 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100618 if hasattr(cls, 'vapi'):
Ole Troanfd574082019-11-27 23:12:48 +0100619 cls.logger.debug(cls.vapi.vpp.get_stats())
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700620 cls.logger.debug("Disconnecting class vapi client on %s",
621 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100622 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700623 cls.logger.debug("Deleting class vapi attribute on %s",
624 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100625 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200626 cls.vpp.poll()
627 if cls.vpp.returncode is None:
Andrew Yourtchenkob31d3932019-10-31 08:34:22 +0000628 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100629 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400630 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100631 cls.logger.debug("Waiting for vpp to die")
632 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700633 cls.logger.debug("Deleting class vpp attribute on %s",
634 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200635 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200636
Klement Sekera3747c752017-04-10 06:30:17 +0200637 if cls.vpp_startup_failed:
638 stdout_log = cls.logger.info
639 stderr_log = cls.logger.critical
640 else:
641 stdout_log = cls.logger.info
642 stderr_log = cls.logger.info
643
Klement Sekerae4504c62016-12-08 10:16:41 +0100644 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200645 stdout_log(single_line_delim)
646 stdout_log('VPP output to stdout while running %s:', cls.__name__)
647 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100648 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200649 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
650 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200651 stdout_log('\n%s', vpp_output)
652 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200653
Klement Sekerae4504c62016-12-08 10:16:41 +0100654 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200655 stderr_log(single_line_delim)
656 stderr_log('VPP output to stderr while running %s:', cls.__name__)
657 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100658 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200659 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
660 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200661 stderr_log('\n%s', vpp_output)
662 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200663
Damjan Marionf56b77a2016-10-03 19:44:57 +0200664 @classmethod
665 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200666 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700667 cls.logger.debug("--- tearDownClass() for %s called ---" %
668 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200669 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200670 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200671 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100672 cls.reset_packet_infos()
673 if debug_framework:
674 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200675
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700676 def show_commands_at_teardown(self):
677 """ Allow subclass specific teardown logging additions."""
678 self.logger.info("--- No test specific show commands provided. ---")
679
Damjan Marionf56b77a2016-10-03 19:44:57 +0200680 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200681 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100682 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
683 (self.__class__.__name__, self._testMethodName,
684 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700685
686 try:
687 if not self.vpp_dead:
688 self.logger.debug(self.vapi.cli("show trace max 1000"))
689 self.logger.info(self.vapi.ppcli("show interface"))
690 self.logger.info(self.vapi.ppcli("show hardware"))
691 self.logger.info(self.statistics.set_errors_str())
692 self.logger.info(self.vapi.ppcli("show run"))
693 self.logger.info(self.vapi.ppcli("show log"))
Dave Barach32dcd3b2019-07-08 12:25:38 -0400694 self.logger.info(self.vapi.ppcli("show bihash"))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700695 self.logger.info("Logging testcase specific show commands.")
696 self.show_commands_at_teardown()
697 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500698 # Save/Dump VPP api trace log
Andrew Yourtchenko586d3ed2019-10-21 12:55:48 +0000699 m = self._testMethodName
700 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
Dave Wallace90c55722017-02-16 11:25:26 -0500701 tmp_api_trace = "/tmp/%s" % api_trace
702 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
703 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
704 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
705 vpp_api_trace_log))
706 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500707 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500708 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700709 except VppTransportShmemIOError:
710 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
711 "Cannot log show commands.")
712 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100713 else:
714 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200715
Damjan Marionf56b77a2016-10-03 19:44:57 +0200716 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200717 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800718 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200719 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100720 if self.vpp_dead:
Paul Vinciguerrafea82602019-06-26 20:45:08 -0400721
722 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
723 method_name=self._testMethodName)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100724 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100725 self.vpp_stdout_deque.append(
726 "--- test setUp() for %s.%s(%s) starts here ---\n" %
727 (self.__class__.__name__, self._testMethodName,
728 self._testMethodDoc))
729 self.vpp_stderr_deque.append(
730 "--- test setUp() for %s.%s(%s) starts here ---\n" %
731 (self.__class__.__name__, self._testMethodName,
732 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200733 self.vapi.cli("clear trace")
734 # store the test instance inside the test class - so that objects
735 # holding the class can access instance methods (like assertEqual)
736 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200737
Damjan Marionf56b77a2016-10-03 19:44:57 +0200738 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200739 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200740 """
741 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200742
Klement Sekera75e7d132017-09-20 08:26:30 +0200743 :param interfaces: iterable interface indexes (if None,
744 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200745
Klement Sekeraf62ae122016-10-11 11:47:09 +0200746 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200747 if interfaces is None:
748 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200749 for i in interfaces:
750 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200751
Damjan Marionf56b77a2016-10-03 19:44:57 +0200752 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100753 def register_capture(cls, cap_name):
754 """ Register a capture in the testclass """
755 # add to the list of captures with current timestamp
756 cls._captures.append((time.time(), cap_name))
Klement Sekera9225dee2016-12-12 08:36:58 +0100757
758 @classmethod
Andrew Yourtchenko63cb8822019-10-13 18:56:03 +0000759 def get_vpp_time(cls):
760 return float(cls.vapi.cli('show clock').replace("Time now ", ""))
761
762 @classmethod
763 def sleep_on_vpp_time(cls, sec):
764 """ Sleep according to time in VPP world """
765 # On a busy system with many processes
766 # we might end up with VPP time being slower than real world
767 # So take that into account when waiting for VPP to do something
768 start_time = cls.get_vpp_time()
769 while cls.get_vpp_time() - start_time < sec:
770 cls.sleep(0.1)
771
772 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100773 def pg_start(cls):
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000774 """ Enable the PG, wait till it is done, then clean up """
Klement Sekerad91fa612019-01-15 13:25:09 +0100775 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200776 cls.vapi.cli('packet-generator enable')
Andrew Yourtchenko8d829f62019-10-13 10:09:50 +0000777 # PG, when starts, runs to completion -
778 # so let's avoid a race condition,
779 # and wait a little till it's done.
780 # Then clean it up - and then be gone.
781 deadline = time.time() + 300
782 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
783 cls.sleep(0.01) # yield
784 if time.time() > deadline:
785 cls.logger.error("Timeout waiting for pg to stop")
786 break
787 for stamp, cap_name in cls._captures:
788 cls.vapi.cli('packet-generator delete %s' % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100789 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200790
Damjan Marionf56b77a2016-10-03 19:44:57 +0200791 @classmethod
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200792 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200793 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100794 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200795
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100796 :param interfaces: iterable indexes of the interfaces.
797 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200798
Klement Sekeraf62ae122016-10-11 11:47:09 +0200799 """
800 result = []
801 for i in interfaces:
Mohsin Kazmi22e9cfd2019-07-23 11:54:48 +0200802 intf = VppPGInterface(cls, i, gso, gso_size)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200803 setattr(cls, intf.name, intf)
804 result.append(intf)
805 cls.pg_interfaces = result
806 return result
807
Matej Klotton0178d522016-11-04 11:11:44 +0100808 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200809 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100810 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100811 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100812
Klement Sekerab9ef2732018-06-24 22:49:33 +0200813 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100814 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100815 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200816 result = [VppLoInterface(cls) for i in range(count)]
817 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100818 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100819 cls.lo_interfaces = result
820 return result
821
Neale Ranns192b13f2019-03-15 02:16:20 -0700822 @classmethod
823 def create_bvi_interfaces(cls, count):
824 """
825 Create BVI interfaces.
826
827 :param count: number of interfaces created.
828 :returns: List of created interfaces.
829 """
830 result = [VppBviInterface(cls) for i in range(count)]
831 for intf in result:
832 setattr(cls, intf.name, intf)
833 cls.bvi_interfaces = result
834 return result
835
Damjan Marionf56b77a2016-10-03 19:44:57 +0200836 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200837 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200838 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200839 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200840 NOTE: Currently works only when Raw layer is present.
841
842 :param packet: packet
843 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200844 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200845
846 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200847 packet_len = len(packet) + 4
848 extend = size - packet_len
849 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200850 num = (extend // len(padding)) + 1
851 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200852
Klement Sekeradab231a2016-12-21 08:50:14 +0100853 @classmethod
854 def reset_packet_infos(cls):
855 """ Reset the list of packet info objects and packet counts to zero """
856 cls._packet_infos = {}
857 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200858
Klement Sekeradab231a2016-12-21 08:50:14 +0100859 @classmethod
860 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200861 """
862 Create packet info object containing the source and destination indexes
863 and add it to the testcase's packet info list
864
Klement Sekeradab231a2016-12-21 08:50:14 +0100865 :param VppInterface src_if: source interface
866 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200867
868 :returns: _PacketInfo object
869
870 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200871 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100872 info.index = len(cls._packet_infos)
873 info.src = src_if.sw_if_index
874 info.dst = dst_if.sw_if_index
875 if isinstance(dst_if, VppSubInterface):
876 dst_idx = dst_if.parent.sw_if_index
877 else:
878 dst_idx = dst_if.sw_if_index
879 if dst_idx in cls._packet_count_for_dst_if_idx:
880 cls._packet_count_for_dst_if_idx[dst_idx] += 1
881 else:
882 cls._packet_count_for_dst_if_idx[dst_idx] = 1
883 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200884 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200885
Damjan Marionf56b77a2016-10-03 19:44:57 +0200886 @staticmethod
887 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200888 """
889 Convert _PacketInfo object to packet payload
890
891 :param info: _PacketInfo object
892
893 :returns: string containing serialized data from packet info
894 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100895 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
896 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200897
Damjan Marionf56b77a2016-10-03 19:44:57 +0200898 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800899 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200900 """
901 Convert packet payload to _PacketInfo object
902
903 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700904 :type payload: <class 'scapy.packet.Raw'>
905 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800906 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700907 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200908 :returns: _PacketInfo object containing de-serialized data from payload
909
910 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800911 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200912 info = _PacketInfo()
913 info.index = int(numbers[0])
914 info.src = int(numbers[1])
915 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100916 info.ip = int(numbers[3])
917 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200918 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200919
Damjan Marionf56b77a2016-10-03 19:44:57 +0200920 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200921 """
922 Iterate over the packet info list stored in the testcase
923 Start iteration with first element if info is None
924 Continue based on index in info if info is specified
925
926 :param info: info or None
927 :returns: next info in list or None if no more infos
928 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200929 if info is None:
930 next_index = 0
931 else:
932 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100933 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200934 return None
935 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100936 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200937
Klement Sekeraf62ae122016-10-11 11:47:09 +0200938 def get_next_packet_info_for_interface(self, src_index, info):
939 """
940 Search the packet info list for the next packet info with same source
941 interface index
942
943 :param src_index: source interface index to search for
944 :param info: packet info - where to start the search
945 :returns: packet info or None
946
947 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200948 while True:
949 info = self.get_next_packet_info(info)
950 if info is None:
951 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200952 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200953 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200954
Klement Sekeraf62ae122016-10-11 11:47:09 +0200955 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
956 """
957 Search the packet info list for the next packet info with same source
958 and destination interface indexes
959
960 :param src_index: source interface index to search for
961 :param dst_index: destination interface index to search for
962 :param info: packet info - where to start the search
963 :returns: packet info or None
964
965 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200966 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200967 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200968 if info is None:
969 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200970 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200971 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200972
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200973 def assert_equal(self, real_value, expected_value, name_or_class=None):
974 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100975 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200976 return
977 try:
978 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
979 msg = msg % (getdoc(name_or_class).strip(),
980 real_value, str(name_or_class(real_value)),
981 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100982 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200983 msg = "Invalid %s: %s does not match expected value %s" % (
984 name_or_class, real_value, expected_value)
985
986 self.assertEqual(real_value, expected_value, msg)
987
Klement Sekerab17dd962017-01-09 07:43:48 +0100988 def assert_in_range(self,
989 real_value,
990 expected_min,
991 expected_max,
992 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200993 if name is None:
994 msg = None
995 else:
996 msg = "Invalid %s: %s out of range <%s,%s>" % (
997 name, real_value, expected_min, expected_max)
998 self.assertTrue(expected_min <= real_value <= expected_max, msg)
999
Klement Sekerad81ae412018-05-16 10:52:54 +02001000 def assert_packet_checksums_valid(self, packet,
1001 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001002 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001003 udp_layers = ['UDP', 'UDPerror']
1004 checksum_fields = ['cksum', 'chksum']
1005 checksums = []
1006 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001007 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +02001008 while True:
1009 layer = temp.getlayer(counter)
1010 if layer:
Klement Sekera66cea092019-12-05 13:13:21 +00001011 layer = layer.copy()
1012 layer.remove_payload()
Klement Sekerad81ae412018-05-16 10:52:54 +02001013 for cf in checksum_fields:
1014 if hasattr(layer, cf):
1015 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +02001016 0 == getattr(layer, cf) and \
1017 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +02001018 continue
Klement Sekera66cea092019-12-05 13:13:21 +00001019 delattr(temp.getlayer(counter), cf)
Klement Sekerad81ae412018-05-16 10:52:54 +02001020 checksums.append((counter, cf))
1021 else:
1022 break
1023 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +02001024 if 0 == len(checksums):
1025 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001026 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +02001027 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +02001028 calc_sum = getattr(temp[layer], cf)
1029 self.assert_equal(
1030 getattr(received[layer], cf), calc_sum,
1031 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1032 self.logger.debug(
1033 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1034 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +02001035
1036 def assert_checksum_valid(self, received_packet, layer,
1037 field_name='chksum',
1038 ignore_zero_checksum=False):
1039 """ Check checksum of received packet on given layer """
1040 received_packet_checksum = getattr(received_packet[layer], field_name)
1041 if ignore_zero_checksum and 0 == received_packet_checksum:
1042 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001043 recalculated = received_packet.__class__(
1044 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001045 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001046 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001047 self.assert_equal(received_packet_checksum,
1048 getattr(recalculated[layer], field_name),
1049 "packet checksum on layer: %s" % layer)
1050
1051 def assert_ip_checksum_valid(self, received_packet,
1052 ignore_zero_checksum=False):
1053 self.assert_checksum_valid(received_packet, 'IP',
1054 ignore_zero_checksum=ignore_zero_checksum)
1055
1056 def assert_tcp_checksum_valid(self, received_packet,
1057 ignore_zero_checksum=False):
1058 self.assert_checksum_valid(received_packet, 'TCP',
1059 ignore_zero_checksum=ignore_zero_checksum)
1060
1061 def assert_udp_checksum_valid(self, received_packet,
1062 ignore_zero_checksum=True):
1063 self.assert_checksum_valid(received_packet, 'UDP',
1064 ignore_zero_checksum=ignore_zero_checksum)
1065
1066 def assert_embedded_icmp_checksum_valid(self, received_packet):
1067 if received_packet.haslayer(IPerror):
1068 self.assert_checksum_valid(received_packet, 'IPerror')
1069 if received_packet.haslayer(TCPerror):
1070 self.assert_checksum_valid(received_packet, 'TCPerror')
1071 if received_packet.haslayer(UDPerror):
1072 self.assert_checksum_valid(received_packet, 'UDPerror',
1073 ignore_zero_checksum=True)
1074 if received_packet.haslayer(ICMPerror):
1075 self.assert_checksum_valid(received_packet, 'ICMPerror')
1076
1077 def assert_icmp_checksum_valid(self, received_packet):
1078 self.assert_checksum_valid(received_packet, 'ICMP')
1079 self.assert_embedded_icmp_checksum_valid(received_packet)
1080
1081 def assert_icmpv6_checksum_valid(self, pkt):
1082 if pkt.haslayer(ICMPv6DestUnreach):
1083 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1084 self.assert_embedded_icmp_checksum_valid(pkt)
1085 if pkt.haslayer(ICMPv6EchoRequest):
1086 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1087 if pkt.haslayer(ICMPv6EchoReply):
1088 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1089
Klement Sekera3a343d42019-05-16 14:35:46 +02001090 def get_packet_counter(self, counter):
1091 if counter.startswith("/"):
1092 counter_value = self.statistics.get_counter(counter)
1093 else:
1094 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001095 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001096 for i in range(1, len(counters) - 1):
1097 results = counters[i].split()
1098 if results[1] == counter:
1099 counter_value = int(results[0])
1100 break
1101 return counter_value
1102
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001103 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001104 counter_value = self.get_packet_counter(counter)
1105 self.assert_equal(counter_value, expected_value,
1106 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001107
Ole Troan233e4682019-05-16 15:01:34 +02001108 def assert_error_counter_equal(self, counter, expected_value):
1109 counter_value = self.statistics.get_err_counter(counter)
1110 self.assert_equal(counter_value, expected_value,
1111 "error counter `%s'" % counter)
1112
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001113 @classmethod
1114 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001115
1116 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1117 # * by Guido, only the main thread can be interrupted.
1118 # */
1119 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1120 if timeout == 0:
1121 # yield quantum
1122 if hasattr(os, 'sched_yield'):
1123 os.sched_yield()
1124 else:
1125 time.sleep(0)
1126 return
1127
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001128 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001129 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001130 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001131 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001132 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001133 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001134 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001135 "slept for %es instead of ~%es!",
1136 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001137 if hasattr(cls, 'logger'):
1138 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001139 "Finished sleep (%s) - slept %es (wanted %es)",
1140 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001141
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001142 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001143 self.vapi.cli("clear trace")
1144 intf.add_stream(pkts)
1145 self.pg_enable_capture(self.pg_interfaces)
1146 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001147
1148 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1149 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001150 if not timeout:
1151 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001152 for i in self.pg_interfaces:
1153 i.get_capture(0, timeout=timeout)
1154 i.assert_nothing_captured(remark=remark)
1155 timeout = 0.1
1156
Neale Rannsd7603d92019-03-28 08:56:10 +00001157 def send_and_expect(self, intf, pkts, output, n_rx=None):
1158 if not n_rx:
1159 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001160 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001161 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001162 return rx
1163
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001164 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1165 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001166 rx = output.get_capture(len(pkts))
1167 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001168 if not timeout:
1169 timeout = 1
1170 for i in self.pg_interfaces:
1171 if i not in outputs:
1172 i.get_capture(0, timeout=timeout)
1173 i.assert_nothing_captured()
1174 timeout = 0.1
1175
Neale Ranns52fae862018-01-08 04:41:42 -08001176 return rx
1177
Damjan Marionf56b77a2016-10-03 19:44:57 +02001178
juraj.linkes184870a2018-07-16 14:22:01 +02001179def get_testcase_doc_name(test):
1180 return getdoc(test.__class__).splitlines()[0]
1181
1182
Ole Trøan5ba91592018-11-22 10:01:09 +00001183def get_test_description(descriptions, test):
1184 short_description = test.shortDescription()
1185 if descriptions and short_description:
1186 return short_description
1187 else:
1188 return str(test)
1189
1190
juraj.linkes40dd73b2018-09-21 13:55:16 +02001191class TestCaseInfo(object):
1192 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1193 self.logger = logger
1194 self.tempdir = tempdir
1195 self.vpp_pid = vpp_pid
1196 self.vpp_bin_path = vpp_bin_path
1197 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001198
1199
Damjan Marionf56b77a2016-10-03 19:44:57 +02001200class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001201 """
1202 @property result_string
1203 String variable to store the test case result string.
1204 @property errors
1205 List variable containing 2-tuples of TestCase instances and strings
1206 holding formatted tracebacks. Each tuple represents a test which
1207 raised an unexpected exception.
1208 @property failures
1209 List variable containing 2-tuples of TestCase instances and strings
1210 holding formatted tracebacks. Each tuple represents a test where
1211 a failure was explicitly signalled using the TestCase.assert*()
1212 methods.
1213 """
1214
juraj.linkes40dd73b2018-09-21 13:55:16 +02001215 failed_test_cases_info = set()
1216 core_crash_test_cases_info = set()
1217 current_test_case_info = None
1218
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001219 def __init__(self, stream=None, descriptions=None, verbosity=None,
1220 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001221 """
Klement Sekerada505f62017-01-04 12:58:53 +01001222 :param stream File descriptor to store where to report test results.
1223 Set to the standard error stream by default.
1224 :param descriptions Boolean variable to store information if to use
1225 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001226 :param verbosity Integer variable to store required verbosity level.
1227 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001228 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001229 self.stream = stream
1230 self.descriptions = descriptions
1231 self.verbosity = verbosity
1232 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001233 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001234
Damjan Marionf56b77a2016-10-03 19:44:57 +02001235 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001236 """
1237 Record a test succeeded result
1238
1239 :param test:
1240
1241 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001242 if self.current_test_case_info:
1243 self.current_test_case_info.logger.debug(
1244 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1245 test._testMethodName,
1246 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001247 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001248 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001249
juraj.linkescae64f82018-09-19 15:01:47 +02001250 self.send_result_through_pipe(test, PASS)
1251
Klement Sekeraf62ae122016-10-11 11:47:09 +02001252 def addSkip(self, test, reason):
1253 """
1254 Record a test skipped.
1255
1256 :param test:
1257 :param reason:
1258
1259 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001260 if self.current_test_case_info:
1261 self.current_test_case_info.logger.debug(
1262 "--- addSkip() %s.%s(%s) called, reason is %s" %
1263 (test.__class__.__name__, test._testMethodName,
1264 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001265 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001266 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001267
juraj.linkescae64f82018-09-19 15:01:47 +02001268 self.send_result_through_pipe(test, SKIP)
1269
juraj.linkes40dd73b2018-09-21 13:55:16 +02001270 def symlink_failed(self):
1271 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001272 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001273 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001274 link_path = os.path.join(
1275 failed_dir,
1276 '%s-FAILED' %
1277 os.path.basename(self.current_test_case_info.tempdir))
1278 if self.current_test_case_info.logger:
1279 self.current_test_case_info.logger.debug(
1280 "creating a link to the failed test")
1281 self.current_test_case_info.logger.debug(
1282 "os.symlink(%s, %s)" %
1283 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001284 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001285 if self.current_test_case_info.logger:
1286 self.current_test_case_info.logger.debug(
1287 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001288 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001289 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001290
Klement Sekeraf413bef2017-08-15 07:09:02 +02001291 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001292 if self.current_test_case_info.logger:
1293 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001294
juraj.linkescae64f82018-09-19 15:01:47 +02001295 def send_result_through_pipe(self, test, result):
1296 if hasattr(self, 'test_framework_result_pipe'):
1297 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001298 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001299 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001300
juraj.linkes40dd73b2018-09-21 13:55:16 +02001301 def log_error(self, test, err, fn_name):
1302 if self.current_test_case_info:
1303 if isinstance(test, unittest.suite._ErrorHolder):
1304 test_name = test.description
1305 else:
1306 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1307 test._testMethodName,
1308 test._testMethodDoc)
1309 self.current_test_case_info.logger.debug(
1310 "--- %s() %s called, err is %s" %
1311 (fn_name, test_name, err))
1312 self.current_test_case_info.logger.debug(
1313 "formatted exception is:\n%s" %
1314 "".join(format_exception(*err)))
1315
1316 def add_error(self, test, err, unittest_fn, error_type):
1317 if error_type == FAIL:
1318 self.log_error(test, err, 'addFailure')
1319 error_type_str = colorize("FAIL", RED)
1320 elif error_type == ERROR:
1321 self.log_error(test, err, 'addError')
1322 error_type_str = colorize("ERROR", RED)
1323 else:
1324 raise Exception('Error type %s cannot be used to record an '
1325 'error or a failure' % error_type)
1326
1327 unittest_fn(self, test, err)
1328 if self.current_test_case_info:
1329 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1330 (error_type_str,
1331 self.current_test_case_info.tempdir)
1332 self.symlink_failed()
1333 self.failed_test_cases_info.add(self.current_test_case_info)
1334 if is_core_present(self.current_test_case_info.tempdir):
1335 if not self.current_test_case_info.core_crash_test:
1336 if isinstance(test, unittest.suite._ErrorHolder):
1337 test_name = str(test)
1338 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001339 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001340 get_testcase_doc_name(test), test.id())
1341 self.current_test_case_info.core_crash_test = test_name
1342 self.core_crash_test_cases_info.add(
1343 self.current_test_case_info)
1344 else:
1345 self.result_string = '%s [no temp dir]' % error_type_str
1346
1347 self.send_result_through_pipe(test, error_type)
1348
Damjan Marionf56b77a2016-10-03 19:44:57 +02001349 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001350 """
1351 Record a test failed result
1352
1353 :param test:
1354 :param err: error message
1355
1356 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001357 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001358
Damjan Marionf56b77a2016-10-03 19:44:57 +02001359 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001360 """
1361 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001362
Klement Sekeraf62ae122016-10-11 11:47:09 +02001363 :param test:
1364 :param err: error message
1365
1366 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001367 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001368
Damjan Marionf56b77a2016-10-03 19:44:57 +02001369 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001370 """
1371 Get test description
1372
1373 :param test:
1374 :returns: test description
1375
1376 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001377 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001378
Damjan Marionf56b77a2016-10-03 19:44:57 +02001379 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001380 """
1381 Start a test
1382
1383 :param test:
1384
1385 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001386
1387 def print_header(test):
1388 if not hasattr(test.__class__, '_header_printed'):
1389 print(double_line_delim)
1390 print(colorize(getdoc(test).splitlines()[0], GREEN))
1391 print(double_line_delim)
1392 test.__class__._header_printed = True
1393
1394 print_header(test)
Ole Troan0c629322019-11-28 14:48:44 +01001395 self.start_test = time.time()
Damjan Marionf56b77a2016-10-03 19:44:57 +02001396 unittest.TestResult.startTest(self, test)
1397 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001398 self.stream.writeln(
1399 "Starting " + self.getDescription(test) + " ...")
1400 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001401
Damjan Marionf56b77a2016-10-03 19:44:57 +02001402 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001403 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001404 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001405
1406 :param test:
1407
1408 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001409 unittest.TestResult.stopTest(self, test)
Ole Troan0c629322019-11-28 14:48:44 +01001410
Damjan Marionf56b77a2016-10-03 19:44:57 +02001411 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:
Ole Troan0c629322019-11-28 14:48:44 +01001417 self.stream.writeln("%-68s %4.2f %s" %
1418 (self.getDescription(test),
1419 time.time() - self.start_test,
1420 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001421
1422 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001423
Damjan Marionf56b77a2016-10-03 19:44:57 +02001424 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001425 """
1426 Print errors from running the test case
1427 """
juraj.linkesabec0122018-11-16 17:28:56 +01001428 if len(self.errors) > 0 or len(self.failures) > 0:
1429 self.stream.writeln()
1430 self.printErrorList('ERROR', self.errors)
1431 self.printErrorList('FAIL', self.failures)
1432
1433 # ^^ that is the last output from unittest before summary
1434 if not self.runner.print_summary:
1435 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1436 self.stream = devnull
1437 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001438
Damjan Marionf56b77a2016-10-03 19:44:57 +02001439 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001440 """
1441 Print error list to the output stream together with error type
1442 and test case description.
1443
1444 :param flavour: error type
1445 :param errors: iterable errors
1446
1447 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001448 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001449 self.stream.writeln(double_line_delim)
1450 self.stream.writeln("%s: %s" %
1451 (flavour, self.getDescription(test)))
1452 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001453 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001454
1455
Damjan Marionf56b77a2016-10-03 19:44:57 +02001456class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001457 """
Klement Sekera104543f2017-02-03 07:29:43 +01001458 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001459 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001460
Klement Sekeraf62ae122016-10-11 11:47:09 +02001461 @property
1462 def resultclass(self):
1463 """Class maintaining the results of the tests"""
1464 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001465
juraj.linkes184870a2018-07-16 14:22:01 +02001466 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001467 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001468 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001469 # ignore stream setting here, use hard-coded stdout to be in sync
1470 # with prints from VppTestCase methods ...
1471 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1472 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001473 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001474 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001475
juraj.linkesabec0122018-11-16 17:28:56 +01001476 self.orig_stream = self.stream
1477 self.resultclass.test_framework_result_pipe = result_pipe
1478
1479 self.print_summary = print_summary
1480
1481 def _makeResult(self):
1482 return self.resultclass(self.stream,
1483 self.descriptions,
1484 self.verbosity,
1485 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001486
Damjan Marionf56b77a2016-10-03 19:44:57 +02001487 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001488 """
1489 Run the tests
1490
1491 :param test:
1492
1493 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001494 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001495
1496 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001497 if not self.print_summary:
1498 self.stream = self.orig_stream
1499 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001500 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001501
1502
1503class Worker(Thread):
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001504 def __init__(self, args, logger, env=None):
Neale Ranns812ed392017-10-16 04:20:13 -07001505 self.logger = logger
1506 self.args = args
Dave Wallace24564332019-10-21 02:53:14 +00001507 if hasattr(self, 'testcase') and self.testcase.debug_all:
1508 if self.testcase.debug_gdbserver:
1509 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1510 .format(port=self.testcase.gdbserver_port)] + args
1511 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1512 self.args.append(self.wait_for_gdb)
1513 self.app_bin = args[0]
1514 self.app_name = os.path.basename(self.app_bin)
1515 if hasattr(self, 'role'):
1516 self.app_name += ' {role}'.format(role=self.role)
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001517 self.process = None
Neale Ranns812ed392017-10-16 04:20:13 -07001518 self.result = None
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001519 env = {} if env is None else env
Dave Wallace42996c02018-02-26 14:40:13 -05001520 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001521 super(Worker, self).__init__()
1522
Dave Wallace24564332019-10-21 02:53:14 +00001523 def wait_for_enter(self):
1524 if not hasattr(self, 'testcase'):
1525 return
1526 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1527 print()
1528 print(double_line_delim)
1529 print("Spawned GDB Server for '{app}' with PID: {pid}"
1530 .format(app=self.app_name, pid=self.process.pid))
1531 elif self.testcase.debug_all and self.testcase.debug_gdb:
1532 print()
1533 print(double_line_delim)
1534 print("Spawned '{app}' with PID: {pid}"
1535 .format(app=self.app_name, pid=self.process.pid))
1536 else:
1537 return
1538 print(single_line_delim)
1539 print("You can debug '{app}' using:".format(app=self.app_name))
1540 if self.testcase.debug_gdbserver:
1541 print("sudo gdb " + self.app_bin +
1542 " -ex 'target remote localhost:{port}'"
1543 .format(port=self.testcase.gdbserver_port))
1544 print("Now is the time to attach gdb by running the above "
1545 "command, set up breakpoints etc., then resume from "
1546 "within gdb by issuing the 'continue' command")
1547 self.testcase.gdbserver_port += 1
1548 elif self.testcase.debug_gdb:
1549 print("sudo gdb " + self.app_bin +
1550 " -ex 'attach {pid}'".format(pid=self.process.pid))
1551 print("Now is the time to attach gdb by running the above "
1552 "command and set up breakpoints etc., then resume from"
1553 " within gdb by issuing the 'continue' command")
1554 print(single_line_delim)
1555 input("Press ENTER to continue running the testcase...")
1556
Neale Ranns812ed392017-10-16 04:20:13 -07001557 def run(self):
1558 executable = self.args[0]
Paul Vinciguerra063366e2019-06-30 15:38:55 -04001559 if not os.path.exists(executable) or not os.access(
1560 executable, os.F_OK | os.X_OK):
1561 # Exit code that means some system file did not exist,
1562 # could not be opened, or had some other kind of error.
1563 self.result = os.EX_OSFILE
1564 raise EnvironmentError(
1565 "executable '%s' is not found or executable." % executable)
Dave Wallace24564332019-10-21 02:53:14 +00001566 self.logger.debug("Running executable: '{app}'"
1567 .format(app=' '.join(self.args)))
Neale Ranns812ed392017-10-16 04:20:13 -07001568 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001569 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001570 env["CK_LOG_FILE_NAME"] = "-"
1571 self.process = subprocess.Popen(
1572 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1573 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Dave Wallace24564332019-10-21 02:53:14 +00001574 self.wait_for_enter()
Neale Ranns812ed392017-10-16 04:20:13 -07001575 out, err = self.process.communicate()
Dave Wallace24564332019-10-21 02:53:14 +00001576 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001577 self.logger.info("Return code is `%s'" % self.process.returncode)
1578 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001579 self.logger.info("Executable `{app}' wrote to stdout:"
1580 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001581 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001582 self.logger.info(out.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001583 self.logger.info(single_line_delim)
Dave Wallace24564332019-10-21 02:53:14 +00001584 self.logger.info("Executable `{app}' wrote to stderr:"
1585 .format(app=self.app_name))
Neale Ranns812ed392017-10-16 04:20:13 -07001586 self.logger.info(single_line_delim)
Dave Wallace97ea2f42019-10-29 19:12:03 -04001587 self.logger.info(err.decode('utf-8'))
Neale Ranns812ed392017-10-16 04:20:13 -07001588 self.logger.info(single_line_delim)
1589 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001590
Klement Sekera6aa58b72019-05-16 14:34:55 +02001591
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001592if __name__ == '__main__':
1593 pass