blob: 6c1ba5e3f339d63435fcb55fd9e08e52d7fefa26 [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
8import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +02009import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020010import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080011import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000012import random
13import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080014import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010015import platform
Ole Trøan162989e2018-11-26 10:27:50 +000016from collections import deque
17from threading import Thread, Event
18from inspect import getdoc, isclass
19from traceback import format_exception
20from logging import FileHandler, DEBUG, Formatter
21from scapy.packet import Raw
22from hook import StepHook, PollHook, VppDiedError
23from vpp_pg_interface import VppPGInterface
24from vpp_sub_interface import VppSubInterface
25from vpp_lo_interface import VppLoInterface
26from vpp_papi_provider import VppPapiProvider
27from vpp_papi.vpp_stats import VPPStats
28from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
29 get_logger, colorize
30from vpp_object import VppObjectRegistry
31from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020032from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
33from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
34from scapy.layers.inet6 import ICMPv6EchoReply
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010035if os.name == 'posix' and sys.version_info[0] < 3:
36 # using subprocess32 is recommended by python official documentation
37 # @ https://docs.python.org/2/library/subprocess.html
38 import subprocess32 as subprocess
39else:
40 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020041
Klement Sekerad81ae412018-05-16 10:52:54 +020042
juraj.linkescae64f82018-09-19 15:01:47 +020043PASS = 0
44FAIL = 1
45ERROR = 2
46SKIP = 3
47TEST_RUN = 4
48
49
Klement Sekeraebbaf552018-02-17 13:41:33 +010050debug_framework = False
51if os.getenv('TEST_DEBUG', "0") == "1":
52 debug_framework = True
53 import debug_internal
54
55
Klement Sekeraf62ae122016-10-11 11:47:09 +020056"""
57 Test framework module.
58
59 The module provides a set of tools for constructing and running tests and
60 representing the results.
61"""
62
Klement Sekeraf62ae122016-10-11 11:47:09 +020063
Damjan Marionf56b77a2016-10-03 19:44:57 +020064class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020065 """Private class to create packet info object.
66
67 Help process information about the next packet.
68 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020069 """
Matej Klotton86d87c42016-11-11 11:38:55 +010070 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020071 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010072 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020073 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010074 #: Store the index of the destination packet generator interface
75 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020076 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010077 #: Store expected ip version
78 ip = -1
79 #: Store expected upper protocol
80 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010081 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020082 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020083
Matej Klotton16a14cd2016-12-07 15:09:13 +010084 def __eq__(self, other):
85 index = self.index == other.index
86 src = self.src == other.src
87 dst = self.dst == other.dst
88 data = self.data == other.data
89 return index and src and dst and data
90
Klement Sekeraf62ae122016-10-11 11:47:09 +020091
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010092def pump_output(testclass):
93 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +010094 stdout_fragment = ""
95 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -040096 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010097 readable = select.select([testclass.vpp.stdout.fileno(),
98 testclass.vpp.stderr.fileno(),
99 testclass.pump_thread_wakeup_pipe[0]],
100 [], [])[0]
101 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100102 read = os.read(testclass.vpp.stdout.fileno(), 102400)
103 if len(read) > 0:
104 split = read.splitlines(True)
105 if len(stdout_fragment) > 0:
106 split[0] = "%s%s" % (stdout_fragment, split[0])
107 if len(split) > 0 and split[-1].endswith("\n"):
108 limit = None
109 else:
110 limit = -1
111 stdout_fragment = split[-1]
112 testclass.vpp_stdout_deque.extend(split[:limit])
113 if not testclass.cache_vpp_output:
114 for line in split[:limit]:
115 testclass.logger.debug(
116 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100117 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100118 read = os.read(testclass.vpp.stderr.fileno(), 102400)
119 if len(read) > 0:
120 split = read.splitlines(True)
121 if len(stderr_fragment) > 0:
122 split[0] = "%s%s" % (stderr_fragment, split[0])
123 if len(split) > 0 and split[-1].endswith("\n"):
124 limit = None
125 else:
126 limit = -1
127 stderr_fragment = split[-1]
128 testclass.vpp_stderr_deque.extend(split[:limit])
129 if not testclass.cache_vpp_output:
130 for line in split[:limit]:
131 testclass.logger.debug(
132 "VPP STDERR: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100133 # ignoring the dummy pipe here intentionally - the flag will take care
134 # of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200135
136
juraj.linkes68ebc832018-11-29 09:37:08 +0100137def is_skip_aarch64_set():
138 return os.getenv('SKIP_AARCH64', 'n').lower() in ('yes', 'y', '1')
139
140
141def is_platform_aarch64():
142 return platform.machine() == 'aarch64'
143
144
Klement Sekera87134932017-03-07 11:39:27 +0100145def running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100146 s = os.getenv("EXTENDED_TESTS", "n")
147 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100148
149
Klement Sekerad3e671e2017-09-29 12:36:37 +0200150def running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100151 os_id = os.getenv("OS_ID", "")
152 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200153
154
Klement Sekera909a6a12017-08-08 04:33:53 +0200155class KeepAliveReporter(object):
156 """
157 Singleton object which reports test start to parent process
158 """
159 _shared_state = {}
160
161 def __init__(self):
162 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800163 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200164
165 @property
166 def pipe(self):
167 return self._pipe
168
169 @pipe.setter
170 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800171 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200172 raise Exception("Internal error - pipe should only be set once.")
173 self._pipe = pipe
174
juraj.linkes40dd73b2018-09-21 13:55:16 +0200175 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200176 """
177 Write current test tmpdir & desc to keep-alive pipe to signal liveness
178 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200179 if self.pipe is None:
180 # if not running forked..
181 return
182
Klement Sekera909a6a12017-08-08 04:33:53 +0200183 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200184 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200185 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200186 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200187
Dave Wallacee2efd122017-09-30 22:04:21 -0400188 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200189
190
Damjan Marionf56b77a2016-10-03 19:44:57 +0200191class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100192 """This subclass is a base class for VPP test cases that are implemented as
193 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200194 """
195
196 @property
197 def packet_infos(self):
198 """List of packet infos"""
199 return self._packet_infos
200
Klement Sekeradab231a2016-12-21 08:50:14 +0100201 @classmethod
202 def get_packet_count_for_if_idx(cls, dst_if_index):
203 """Get the number of packet info for specified destination if index"""
204 if dst_if_index in cls._packet_count_for_dst_if_idx:
205 return cls._packet_count_for_dst_if_idx[dst_if_index]
206 else:
207 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200208
209 @classmethod
210 def instance(cls):
211 """Return the instance of this testcase"""
212 return cls.test_instance
213
Damjan Marionf56b77a2016-10-03 19:44:57 +0200214 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200215 def set_debug_flags(cls, d):
216 cls.debug_core = False
217 cls.debug_gdb = False
218 cls.debug_gdbserver = False
219 if d is None:
220 return
221 dl = d.lower()
222 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200223 cls.debug_core = True
224 elif dl == "gdb":
225 cls.debug_gdb = True
226 elif dl == "gdbserver":
227 cls.debug_gdbserver = True
228 else:
229 raise Exception("Unrecognized DEBUG option: '%s'" % d)
230
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800231 @staticmethod
232 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200233 cpu_usage_list = [set(range(psutil.cpu_count()))]
234 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
235 if 'vpp_main' == p.info['name']]
236 for vpp_process in vpp_processes:
237 for cpu_usage_set in cpu_usage_list:
238 try:
239 cpu_num = vpp_process.cpu_num()
240 if cpu_num in cpu_usage_set:
241 cpu_usage_set_index = cpu_usage_list.index(
242 cpu_usage_set)
243 if cpu_usage_set_index == len(cpu_usage_list) - 1:
244 cpu_usage_list.append({cpu_num})
245 else:
246 cpu_usage_list[cpu_usage_set_index + 1].add(
247 cpu_num)
248 cpu_usage_set.remove(cpu_num)
249 break
250 except psutil.NoSuchProcess:
251 pass
252
253 for cpu_usage_set in cpu_usage_list:
254 if len(cpu_usage_set) > 0:
255 min_usage_set = cpu_usage_set
256 break
257
258 return random.choice(tuple(min_usage_set))
259
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800260 @classmethod
juraj.linkes40dd73b2018-09-21 13:55:16 +0200261 def print_header(cls):
262 if not hasattr(cls, '_header_printed'):
263 print(double_line_delim)
264 print(colorize(getdoc(cls).splitlines()[0], GREEN))
265 print(double_line_delim)
266 cls._header_printed = True
267
juraj.linkes184870a2018-07-16 14:22:01 +0200268 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200269 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200270 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100271 s = os.getenv("STEP", "n")
272 cls.step = True if s.lower() in ("y", "yes", "1") else False
273 d = os.getenv("DEBUG", None)
274 c = os.getenv("CACHE_OUTPUT", "1")
275 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200276 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100277 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
278 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100279 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
280 plugin_path = None
281 if cls.plugin_path is not None:
282 if cls.extern_plugin_path is not None:
283 plugin_path = "%s:%s" % (
284 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100285 else:
286 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100287 elif cls.extern_plugin_path is not None:
288 plugin_path = cls.extern_plugin_path
Klement Sekera01bbbe92016-11-02 09:25:05 +0100289 debug_cli = ""
290 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
291 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100292 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100293 size = os.getenv("COREDUMP_SIZE")
294 if size is not None:
295 coredump_size = "coredump-size %s" % size
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100296 if coredump_size is None:
Dave Wallacee2efd122017-09-30 22:04:21 -0400297 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200298
299 cpu_core_number = cls.get_least_used_cpu()
300
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100301 cls.vpp_cmdline = [cls.vpp_bin, "unix",
Dave Wallacee2efd122017-09-30 22:04:21 -0400302 "{", "nodaemon", debug_cli, "full-coredump",
Jakub Grajciar99743912018-10-09 12:28:21 +0200303 coredump_size, "runtime-dir", cls.tempdir, "}",
304 "api-trace", "{", "on", "}", "api-segment", "{",
305 "prefix", cls.shm_prefix, "}", "cpu", "{",
306 "main-core", str(cpu_core_number), "}", "statseg",
307 "{", "socket-name", cls.stats_sock, "}", "plugins",
308 "{", "plugin", "dpdk_plugin.so", "{", "disable",
309 "}", "plugin", "unittest_plugin.so", "{", "enable",
310 "}", "}", ]
Klement Sekera47e275b2017-03-21 08:21:25 +0100311 if plugin_path is not None:
312 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100313 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
314 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200315
316 @classmethod
317 def wait_for_enter(cls):
318 if cls.debug_gdbserver:
319 print(double_line_delim)
320 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
321 elif cls.debug_gdb:
322 print(double_line_delim)
323 print("Spawned VPP with PID: %d" % cls.vpp.pid)
324 else:
325 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
326 return
327 print(single_line_delim)
328 print("You can debug the VPP using e.g.:")
329 if cls.debug_gdbserver:
330 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
331 print("Now is the time to attach a gdb by running the above "
332 "command, set up breakpoints etc. and then resume VPP from "
333 "within gdb by issuing the 'continue' command")
334 elif cls.debug_gdb:
335 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
336 print("Now is the time to attach a gdb by running the above "
337 "command and set up breakpoints etc.")
338 print(single_line_delim)
Ole Trøan162989e2018-11-26 10:27:50 +0000339 raw_input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200340
341 @classmethod
342 def run_vpp(cls):
343 cmdline = cls.vpp_cmdline
344
345 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100346 gdbserver = '/usr/bin/gdbserver'
347 if not os.path.isfile(gdbserver) or \
348 not os.access(gdbserver, os.X_OK):
349 raise Exception("gdbserver binary '%s' does not exist or is "
350 "not executable" % gdbserver)
351
352 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200353 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
354
Klement Sekera931be3a2016-11-03 05:36:01 +0100355 try:
356 cls.vpp = subprocess.Popen(cmdline,
357 stdout=subprocess.PIPE,
358 stderr=subprocess.PIPE,
359 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800360 except subprocess.CalledProcessError as e:
Klement Sekera931be3a2016-11-03 05:36:01 +0100361 cls.logger.critical("Couldn't start vpp: %s" % e)
362 raise
363
Klement Sekera277b89c2016-10-28 13:20:27 +0200364 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100365
Damjan Marionf56b77a2016-10-03 19:44:57 +0200366 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200367 def wait_for_stats_socket(cls):
368 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800369 ok = False
370 while time.time() < deadline or \
371 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200372 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800373 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200374 break
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800375 time.sleep(0.8)
376 if not ok:
377 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200378
379 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200380 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200381 """
382 Perform class setup before running the testcase
383 Remove shared memory files, start vpp and connect the vpp-api
384 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100385 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100386 random.seed()
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800387 cls.print_header()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100388 cls.logger = get_logger(cls.__name__)
389 if hasattr(cls, 'parallel_handler'):
390 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100391 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200392 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200393 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200394 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200395 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
396 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100397 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
398 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200399 cls.file_handler.setLevel(DEBUG)
400 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200401 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200402 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200403 cls.logger.info("Temporary dir is %s, shm prefix is %s",
404 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200405 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100406 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100407 cls._captures = []
408 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200409 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100410 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100411 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200412 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200413 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200414 # need to catch exceptions here because if we raise, then the cleanup
415 # doesn't get called and we might end with a zombie vpp
416 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200417 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200418 cls.reporter.send_keep_alive(cls, 'setUpClass')
419 VppTestResult.current_test_case_info = TestCaseInfo(
420 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100421 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100422 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100423 cls.pump_thread_stop_flag = Event()
424 cls.pump_thread_wakeup_pipe = os.pipe()
425 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100426 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100427 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200428 if cls.debug_gdb or cls.debug_gdbserver:
429 read_timeout = 0
430 else:
431 read_timeout = 5
432 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
433 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100434 if cls.step:
435 hook = StepHook(cls)
436 else:
437 hook = PollHook(cls)
438 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200439 cls.wait_for_stats_socket()
440 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200441 try:
442 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100443 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200444 cls.vpp_startup_failed = True
445 cls.logger.critical(
446 "VPP died shortly after startup, check the"
447 " output to standard error for possible cause")
448 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100449 try:
450 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100451 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100452 try:
453 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100454 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100455 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100456 if cls.debug_gdbserver:
457 print(colorize("You're running VPP inside gdbserver but "
458 "VPP-API connection failed, did you forget "
459 "to 'continue' VPP from within gdb?", RED))
460 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100461 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100462 try:
463 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100464 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100465 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100466 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200467
Damjan Marionf56b77a2016-10-03 19:44:57 +0200468 @classmethod
469 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200470 """
471 Disconnect vpp-api, kill vpp and cleanup shared memory files
472 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200473 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
474 cls.vpp.poll()
475 if cls.vpp.returncode is None:
476 print(double_line_delim)
477 print("VPP or GDB server is still running")
478 print(single_line_delim)
Ole Trøan162989e2018-11-26 10:27:50 +0000479 raw_input("When done debugging, press ENTER to kill the "
Klement Sekerae1ace192018-03-23 21:54:12 +0100480 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200481
juraj.linkes184870a2018-07-16 14:22:01 +0200482 # first signal that we want to stop the pump thread, then wake it up
483 if hasattr(cls, 'pump_thread_stop_flag'):
484 cls.pump_thread_stop_flag.set()
485 if hasattr(cls, 'pump_thread_wakeup_pipe'):
486 os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100487 if hasattr(cls, 'pump_thread'):
488 cls.logger.debug("Waiting for pump thread to stop")
489 cls.pump_thread.join()
490 if hasattr(cls, 'vpp_stderr_reader_thread'):
491 cls.logger.debug("Waiting for stdderr pump to stop")
492 cls.vpp_stderr_reader_thread.join()
493
Klement Sekeraf62ae122016-10-11 11:47:09 +0200494 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100495 if hasattr(cls, 'vapi'):
496 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100497 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200498 cls.vpp.poll()
499 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100500 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200501 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100502 cls.logger.debug("Waiting for vpp to die")
503 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200504 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200505
Klement Sekera3747c752017-04-10 06:30:17 +0200506 if cls.vpp_startup_failed:
507 stdout_log = cls.logger.info
508 stderr_log = cls.logger.critical
509 else:
510 stdout_log = cls.logger.info
511 stderr_log = cls.logger.info
512
Klement Sekerae4504c62016-12-08 10:16:41 +0100513 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200514 stdout_log(single_line_delim)
515 stdout_log('VPP output to stdout while running %s:', cls.__name__)
516 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100517 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200518 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
519 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200520 stdout_log('\n%s', vpp_output)
521 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200522
Klement Sekerae4504c62016-12-08 10:16:41 +0100523 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200524 stderr_log(single_line_delim)
525 stderr_log('VPP output to stderr while running %s:', cls.__name__)
526 stderr_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100527 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200528 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
529 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200530 stderr_log('\n%s', vpp_output)
531 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200532
Damjan Marionf56b77a2016-10-03 19:44:57 +0200533 @classmethod
534 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200535 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200536 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200537 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200538 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100539 cls.reset_packet_infos()
540 if debug_framework:
541 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200542
Damjan Marionf56b77a2016-10-03 19:44:57 +0200543 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200544 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100545 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
546 (self.__class__.__name__, self._testMethodName,
547 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200548 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200549 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700550 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200551 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200552 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200553 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800554 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100555 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500556 # Save/Dump VPP api trace log
557 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
558 tmp_api_trace = "/tmp/%s" % api_trace
559 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
560 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
561 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
562 vpp_api_trace_log))
563 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500564 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500565 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100566 else:
567 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200568
Damjan Marionf56b77a2016-10-03 19:44:57 +0200569 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200570 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200571 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100572 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
573 (self.__class__.__name__, self._testMethodName,
574 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100575 if self.vpp_dead:
576 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100577 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100578 self.vpp_stdout_deque.append(
579 "--- test setUp() for %s.%s(%s) starts here ---\n" %
580 (self.__class__.__name__, self._testMethodName,
581 self._testMethodDoc))
582 self.vpp_stderr_deque.append(
583 "--- test setUp() for %s.%s(%s) starts here ---\n" %
584 (self.__class__.__name__, self._testMethodName,
585 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200586 self.vapi.cli("clear trace")
587 # store the test instance inside the test class - so that objects
588 # holding the class can access instance methods (like assertEqual)
589 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200590
Damjan Marionf56b77a2016-10-03 19:44:57 +0200591 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200592 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200593 """
594 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200595
Klement Sekera75e7d132017-09-20 08:26:30 +0200596 :param interfaces: iterable interface indexes (if None,
597 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200598
Klement Sekeraf62ae122016-10-11 11:47:09 +0200599 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200600 if interfaces is None:
601 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200602 for i in interfaces:
603 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200604
Damjan Marionf56b77a2016-10-03 19:44:57 +0200605 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100606 def register_capture(cls, cap_name):
607 """ Register a capture in the testclass """
608 # add to the list of captures with current timestamp
609 cls._captures.append((time.time(), cap_name))
610 # filter out from zombies
611 cls._zombie_captures = [(stamp, name)
612 for (stamp, name) in cls._zombie_captures
613 if name != cap_name]
614
615 @classmethod
616 def pg_start(cls):
617 """ Remove any zombie captures and enable the packet generator """
618 # how long before capture is allowed to be deleted - otherwise vpp
619 # crashes - 100ms seems enough (this shouldn't be needed at all)
620 capture_ttl = 0.1
621 now = time.time()
622 for stamp, cap_name in cls._zombie_captures:
623 wait = stamp + capture_ttl - now
624 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100625 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100626 now = time.time()
627 cls.logger.debug("Removing zombie capture %s" % cap_name)
628 cls.vapi.cli('packet-generator delete %s' % cap_name)
629
Klement Sekeraf62ae122016-10-11 11:47:09 +0200630 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
631 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100632 cls._zombie_captures = cls._captures
633 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200634
Damjan Marionf56b77a2016-10-03 19:44:57 +0200635 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200636 def create_pg_interfaces(cls, interfaces):
637 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100638 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200639
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100640 :param interfaces: iterable indexes of the interfaces.
641 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200642
Klement Sekeraf62ae122016-10-11 11:47:09 +0200643 """
644 result = []
645 for i in interfaces:
646 intf = VppPGInterface(cls, i)
647 setattr(cls, intf.name, intf)
648 result.append(intf)
649 cls.pg_interfaces = result
650 return result
651
Matej Klotton0178d522016-11-04 11:11:44 +0100652 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200653 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100654 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100655 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100656
Klement Sekerab9ef2732018-06-24 22:49:33 +0200657 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100658 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100659 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200660 result = [VppLoInterface(cls) for i in range(count)]
661 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100662 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100663 cls.lo_interfaces = result
664 return result
665
Damjan Marionf56b77a2016-10-03 19:44:57 +0200666 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200667 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200668 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200669 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200670 NOTE: Currently works only when Raw layer is present.
671
672 :param packet: packet
673 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200674 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200675
676 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200677 packet_len = len(packet) + 4
678 extend = size - packet_len
679 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200680 num = (extend / len(padding)) + 1
681 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200682
Klement Sekeradab231a2016-12-21 08:50:14 +0100683 @classmethod
684 def reset_packet_infos(cls):
685 """ Reset the list of packet info objects and packet counts to zero """
686 cls._packet_infos = {}
687 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200688
Klement Sekeradab231a2016-12-21 08:50:14 +0100689 @classmethod
690 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200691 """
692 Create packet info object containing the source and destination indexes
693 and add it to the testcase's packet info list
694
Klement Sekeradab231a2016-12-21 08:50:14 +0100695 :param VppInterface src_if: source interface
696 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200697
698 :returns: _PacketInfo object
699
700 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200701 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100702 info.index = len(cls._packet_infos)
703 info.src = src_if.sw_if_index
704 info.dst = dst_if.sw_if_index
705 if isinstance(dst_if, VppSubInterface):
706 dst_idx = dst_if.parent.sw_if_index
707 else:
708 dst_idx = dst_if.sw_if_index
709 if dst_idx in cls._packet_count_for_dst_if_idx:
710 cls._packet_count_for_dst_if_idx[dst_idx] += 1
711 else:
712 cls._packet_count_for_dst_if_idx[dst_idx] = 1
713 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200714 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200715
Damjan Marionf56b77a2016-10-03 19:44:57 +0200716 @staticmethod
717 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200718 """
719 Convert _PacketInfo object to packet payload
720
721 :param info: _PacketInfo object
722
723 :returns: string containing serialized data from packet info
724 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100725 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
726 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727
Damjan Marionf56b77a2016-10-03 19:44:57 +0200728 @staticmethod
729 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200730 """
731 Convert packet payload to _PacketInfo object
732
733 :param payload: packet payload
734
735 :returns: _PacketInfo object containing de-serialized data from payload
736
737 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200738 numbers = payload.split()
739 info = _PacketInfo()
740 info.index = int(numbers[0])
741 info.src = int(numbers[1])
742 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100743 info.ip = int(numbers[3])
744 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200745 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200746
Damjan Marionf56b77a2016-10-03 19:44:57 +0200747 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200748 """
749 Iterate over the packet info list stored in the testcase
750 Start iteration with first element if info is None
751 Continue based on index in info if info is specified
752
753 :param info: info or None
754 :returns: next info in list or None if no more infos
755 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200756 if info is None:
757 next_index = 0
758 else:
759 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100760 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200761 return None
762 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100763 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200764
Klement Sekeraf62ae122016-10-11 11:47:09 +0200765 def get_next_packet_info_for_interface(self, src_index, info):
766 """
767 Search the packet info list for the next packet info with same source
768 interface index
769
770 :param src_index: source interface index to search for
771 :param info: packet info - where to start the search
772 :returns: packet info or None
773
774 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200775 while True:
776 info = self.get_next_packet_info(info)
777 if info is None:
778 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200779 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200780 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200781
Klement Sekeraf62ae122016-10-11 11:47:09 +0200782 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
783 """
784 Search the packet info list for the next packet info with same source
785 and destination interface indexes
786
787 :param src_index: source interface index to search for
788 :param dst_index: destination interface index to search for
789 :param info: packet info - where to start the search
790 :returns: packet info or None
791
792 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200793 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200794 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200795 if info is None:
796 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200797 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200798 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200799
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200800 def assert_equal(self, real_value, expected_value, name_or_class=None):
801 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100802 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200803 return
804 try:
805 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
806 msg = msg % (getdoc(name_or_class).strip(),
807 real_value, str(name_or_class(real_value)),
808 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100809 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200810 msg = "Invalid %s: %s does not match expected value %s" % (
811 name_or_class, real_value, expected_value)
812
813 self.assertEqual(real_value, expected_value, msg)
814
Klement Sekerab17dd962017-01-09 07:43:48 +0100815 def assert_in_range(self,
816 real_value,
817 expected_min,
818 expected_max,
819 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200820 if name is None:
821 msg = None
822 else:
823 msg = "Invalid %s: %s out of range <%s,%s>" % (
824 name, real_value, expected_min, expected_max)
825 self.assertTrue(expected_min <= real_value <= expected_max, msg)
826
Klement Sekerad81ae412018-05-16 10:52:54 +0200827 def assert_packet_checksums_valid(self, packet,
828 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200829 received = packet.__class__(str(packet))
830 self.logger.debug(
831 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200832 udp_layers = ['UDP', 'UDPerror']
833 checksum_fields = ['cksum', 'chksum']
834 checksums = []
835 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200836 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200837 while True:
838 layer = temp.getlayer(counter)
839 if layer:
840 for cf in checksum_fields:
841 if hasattr(layer, cf):
842 if ignore_zero_udp_checksums and \
843 0 == getattr(layer, cf) and \
844 layer.name in udp_layers:
845 continue
846 delattr(layer, cf)
847 checksums.append((counter, cf))
848 else:
849 break
850 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200851 if 0 == len(checksums):
852 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200853 temp = temp.__class__(str(temp))
854 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200855 calc_sum = getattr(temp[layer], cf)
856 self.assert_equal(
857 getattr(received[layer], cf), calc_sum,
858 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
859 self.logger.debug(
860 "Checksum field `%s` on `%s` layer has correct value `%s`" %
861 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200862
863 def assert_checksum_valid(self, received_packet, layer,
864 field_name='chksum',
865 ignore_zero_checksum=False):
866 """ Check checksum of received packet on given layer """
867 received_packet_checksum = getattr(received_packet[layer], field_name)
868 if ignore_zero_checksum and 0 == received_packet_checksum:
869 return
870 recalculated = received_packet.__class__(str(received_packet))
871 delattr(recalculated[layer], field_name)
872 recalculated = recalculated.__class__(str(recalculated))
873 self.assert_equal(received_packet_checksum,
874 getattr(recalculated[layer], field_name),
875 "packet checksum on layer: %s" % layer)
876
877 def assert_ip_checksum_valid(self, received_packet,
878 ignore_zero_checksum=False):
879 self.assert_checksum_valid(received_packet, 'IP',
880 ignore_zero_checksum=ignore_zero_checksum)
881
882 def assert_tcp_checksum_valid(self, received_packet,
883 ignore_zero_checksum=False):
884 self.assert_checksum_valid(received_packet, 'TCP',
885 ignore_zero_checksum=ignore_zero_checksum)
886
887 def assert_udp_checksum_valid(self, received_packet,
888 ignore_zero_checksum=True):
889 self.assert_checksum_valid(received_packet, 'UDP',
890 ignore_zero_checksum=ignore_zero_checksum)
891
892 def assert_embedded_icmp_checksum_valid(self, received_packet):
893 if received_packet.haslayer(IPerror):
894 self.assert_checksum_valid(received_packet, 'IPerror')
895 if received_packet.haslayer(TCPerror):
896 self.assert_checksum_valid(received_packet, 'TCPerror')
897 if received_packet.haslayer(UDPerror):
898 self.assert_checksum_valid(received_packet, 'UDPerror',
899 ignore_zero_checksum=True)
900 if received_packet.haslayer(ICMPerror):
901 self.assert_checksum_valid(received_packet, 'ICMPerror')
902
903 def assert_icmp_checksum_valid(self, received_packet):
904 self.assert_checksum_valid(received_packet, 'ICMP')
905 self.assert_embedded_icmp_checksum_valid(received_packet)
906
907 def assert_icmpv6_checksum_valid(self, pkt):
908 if pkt.haslayer(ICMPv6DestUnreach):
909 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
910 self.assert_embedded_icmp_checksum_valid(pkt)
911 if pkt.haslayer(ICMPv6EchoRequest):
912 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
913 if pkt.haslayer(ICMPv6EchoReply):
914 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
915
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100916 def assert_packet_counter_equal(self, counter, expected_value):
917 counters = self.vapi.cli("sh errors").split('\n')
918 counter_value = -1
919 for i in range(1, len(counters)-1):
920 results = counters[i].split()
921 if results[1] == counter:
922 counter_value = int(results[0])
923 break
924 self.assert_equal(counter_value, expected_value,
925 "packet counter `%s'" % counter)
926
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100927 @classmethod
928 def sleep(cls, timeout, remark=None):
929 if hasattr(cls, 'logger'):
Klement Sekera3cfa5582017-04-19 07:10:58 +0000930 cls.logger.debug("Starting sleep for %ss (%s)" % (timeout, remark))
931 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100932 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000933 after = time.time()
934 if after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200935 cls.logger.error("unexpected time.sleep() result - "
936 "slept for %ss instead of ~%ss!" % (
937 after - before, timeout))
Klement Sekera3cfa5582017-04-19 07:10:58 +0000938 if hasattr(cls, 'logger'):
939 cls.logger.debug(
940 "Finished sleep (%s) - slept %ss (wanted %ss)" % (
941 remark, after - before, timeout))
Klement Sekeraa57a9702017-02-02 06:58:07 +0100942
Neale Ranns947ea622018-06-07 23:48:20 -0700943 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800944 self.vapi.cli("clear trace")
945 intf.add_stream(pkts)
946 self.pg_enable_capture(self.pg_interfaces)
947 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700948 if not timeout:
949 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800950 for i in self.pg_interfaces:
951 i.get_capture(0, timeout=timeout)
952 i.assert_nothing_captured(remark=remark)
953 timeout = 0.1
954
955 def send_and_expect(self, input, pkts, output):
956 self.vapi.cli("clear trace")
957 input.add_stream(pkts)
958 self.pg_enable_capture(self.pg_interfaces)
959 self.pg_start()
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700960 if isinstance(object, (list,)):
961 rx = []
962 for o in output:
963 rx.append(output.get_capture(len(pkts)))
964 else:
965 rx = output.get_capture(len(pkts))
966 return rx
967
968 def send_and_expect_only(self, input, pkts, output, timeout=None):
969 self.vapi.cli("clear trace")
970 input.add_stream(pkts)
971 self.pg_enable_capture(self.pg_interfaces)
972 self.pg_start()
973 if isinstance(object, (list,)):
974 outputs = output
975 rx = []
976 for o in outputs:
977 rx.append(output.get_capture(len(pkts)))
978 else:
979 rx = output.get_capture(len(pkts))
980 outputs = [output]
981 if not timeout:
982 timeout = 1
983 for i in self.pg_interfaces:
984 if i not in outputs:
985 i.get_capture(0, timeout=timeout)
986 i.assert_nothing_captured()
987 timeout = 0.1
988
Neale Ranns52fae862018-01-08 04:41:42 -0800989 return rx
990
Damjan Marionf56b77a2016-10-03 19:44:57 +0200991
juraj.linkes184870a2018-07-16 14:22:01 +0200992def get_testcase_doc_name(test):
993 return getdoc(test.__class__).splitlines()[0]
994
995
Ole Trøan5ba91592018-11-22 10:01:09 +0000996def get_test_description(descriptions, test):
997 short_description = test.shortDescription()
998 if descriptions and short_description:
999 return short_description
1000 else:
1001 return str(test)
1002
1003
juraj.linkes40dd73b2018-09-21 13:55:16 +02001004class TestCaseInfo(object):
1005 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1006 self.logger = logger
1007 self.tempdir = tempdir
1008 self.vpp_pid = vpp_pid
1009 self.vpp_bin_path = vpp_bin_path
1010 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001011
1012
Damjan Marionf56b77a2016-10-03 19:44:57 +02001013class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001014 """
1015 @property result_string
1016 String variable to store the test case result string.
1017 @property errors
1018 List variable containing 2-tuples of TestCase instances and strings
1019 holding formatted tracebacks. Each tuple represents a test which
1020 raised an unexpected exception.
1021 @property failures
1022 List variable containing 2-tuples of TestCase instances and strings
1023 holding formatted tracebacks. Each tuple represents a test where
1024 a failure was explicitly signalled using the TestCase.assert*()
1025 methods.
1026 """
1027
juraj.linkes40dd73b2018-09-21 13:55:16 +02001028 failed_test_cases_info = set()
1029 core_crash_test_cases_info = set()
1030 current_test_case_info = None
1031
juraj.linkesabec0122018-11-16 17:28:56 +01001032 def __init__(self, stream, descriptions, verbosity, runner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001033 """
Klement Sekerada505f62017-01-04 12:58:53 +01001034 :param stream File descriptor to store where to report test results.
1035 Set to the standard error stream by default.
1036 :param descriptions Boolean variable to store information if to use
1037 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001038 :param verbosity Integer variable to store required verbosity level.
1039 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001040 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
1041 self.stream = stream
1042 self.descriptions = descriptions
1043 self.verbosity = verbosity
1044 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001045 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001046
Damjan Marionf56b77a2016-10-03 19:44:57 +02001047 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001048 """
1049 Record a test succeeded result
1050
1051 :param test:
1052
1053 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001054 if self.current_test_case_info:
1055 self.current_test_case_info.logger.debug(
1056 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1057 test._testMethodName,
1058 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001059 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001060 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001061
juraj.linkescae64f82018-09-19 15:01:47 +02001062 self.send_result_through_pipe(test, PASS)
1063
Klement Sekeraf62ae122016-10-11 11:47:09 +02001064 def addSkip(self, test, reason):
1065 """
1066 Record a test skipped.
1067
1068 :param test:
1069 :param reason:
1070
1071 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001072 if self.current_test_case_info:
1073 self.current_test_case_info.logger.debug(
1074 "--- addSkip() %s.%s(%s) called, reason is %s" %
1075 (test.__class__.__name__, test._testMethodName,
1076 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001077 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001078 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001079
juraj.linkescae64f82018-09-19 15:01:47 +02001080 self.send_result_through_pipe(test, SKIP)
1081
juraj.linkes40dd73b2018-09-21 13:55:16 +02001082 def symlink_failed(self):
1083 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001084 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001085 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001086 link_path = os.path.join(
1087 failed_dir,
1088 '%s-FAILED' %
1089 os.path.basename(self.current_test_case_info.tempdir))
1090 if self.current_test_case_info.logger:
1091 self.current_test_case_info.logger.debug(
1092 "creating a link to the failed test")
1093 self.current_test_case_info.logger.debug(
1094 "os.symlink(%s, %s)" %
1095 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001096 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001097 if self.current_test_case_info.logger:
1098 self.current_test_case_info.logger.debug(
1099 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001100 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001101 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001102
Klement Sekeraf413bef2017-08-15 07:09:02 +02001103 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001104 if self.current_test_case_info.logger:
1105 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001106
juraj.linkescae64f82018-09-19 15:01:47 +02001107 def send_result_through_pipe(self, test, result):
1108 if hasattr(self, 'test_framework_result_pipe'):
1109 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001110 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001111 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001112
juraj.linkes40dd73b2018-09-21 13:55:16 +02001113 def log_error(self, test, err, fn_name):
1114 if self.current_test_case_info:
1115 if isinstance(test, unittest.suite._ErrorHolder):
1116 test_name = test.description
1117 else:
1118 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1119 test._testMethodName,
1120 test._testMethodDoc)
1121 self.current_test_case_info.logger.debug(
1122 "--- %s() %s called, err is %s" %
1123 (fn_name, test_name, err))
1124 self.current_test_case_info.logger.debug(
1125 "formatted exception is:\n%s" %
1126 "".join(format_exception(*err)))
1127
1128 def add_error(self, test, err, unittest_fn, error_type):
1129 if error_type == FAIL:
1130 self.log_error(test, err, 'addFailure')
1131 error_type_str = colorize("FAIL", RED)
1132 elif error_type == ERROR:
1133 self.log_error(test, err, 'addError')
1134 error_type_str = colorize("ERROR", RED)
1135 else:
1136 raise Exception('Error type %s cannot be used to record an '
1137 'error or a failure' % error_type)
1138
1139 unittest_fn(self, test, err)
1140 if self.current_test_case_info:
1141 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1142 (error_type_str,
1143 self.current_test_case_info.tempdir)
1144 self.symlink_failed()
1145 self.failed_test_cases_info.add(self.current_test_case_info)
1146 if is_core_present(self.current_test_case_info.tempdir):
1147 if not self.current_test_case_info.core_crash_test:
1148 if isinstance(test, unittest.suite._ErrorHolder):
1149 test_name = str(test)
1150 else:
1151 test_name = "'{}' ({})".format(
1152 get_testcase_doc_name(test), test.id())
1153 self.current_test_case_info.core_crash_test = test_name
1154 self.core_crash_test_cases_info.add(
1155 self.current_test_case_info)
1156 else:
1157 self.result_string = '%s [no temp dir]' % error_type_str
1158
1159 self.send_result_through_pipe(test, error_type)
1160
Damjan Marionf56b77a2016-10-03 19:44:57 +02001161 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001162 """
1163 Record a test failed result
1164
1165 :param test:
1166 :param err: error message
1167
1168 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001169 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001170
Damjan Marionf56b77a2016-10-03 19:44:57 +02001171 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001172 """
1173 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001174
Klement Sekeraf62ae122016-10-11 11:47:09 +02001175 :param test:
1176 :param err: error message
1177
1178 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001179 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001180
Damjan Marionf56b77a2016-10-03 19:44:57 +02001181 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001182 """
1183 Get test description
1184
1185 :param test:
1186 :returns: test description
1187
1188 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001189 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001190
Damjan Marionf56b77a2016-10-03 19:44:57 +02001191 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001192 """
1193 Start a test
1194
1195 :param test:
1196
1197 """
Paul Vinciguerra86ebba62018-11-21 09:28:32 -08001198 test.print_header()
juraj.linkes40dd73b2018-09-21 13:55:16 +02001199
Damjan Marionf56b77a2016-10-03 19:44:57 +02001200 unittest.TestResult.startTest(self, test)
1201 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001202 self.stream.writeln(
1203 "Starting " + self.getDescription(test) + " ...")
1204 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001205
Damjan Marionf56b77a2016-10-03 19:44:57 +02001206 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001207 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001208 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001209
1210 :param test:
1211
1212 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001213 unittest.TestResult.stopTest(self, test)
1214 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001215 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001216 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001217 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001218 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001219 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001220 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001221 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001222
1223 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001224
Damjan Marionf56b77a2016-10-03 19:44:57 +02001225 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001226 """
1227 Print errors from running the test case
1228 """
juraj.linkesabec0122018-11-16 17:28:56 +01001229 if len(self.errors) > 0 or len(self.failures) > 0:
1230 self.stream.writeln()
1231 self.printErrorList('ERROR', self.errors)
1232 self.printErrorList('FAIL', self.failures)
1233
1234 # ^^ that is the last output from unittest before summary
1235 if not self.runner.print_summary:
1236 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1237 self.stream = devnull
1238 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001239
Damjan Marionf56b77a2016-10-03 19:44:57 +02001240 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001241 """
1242 Print error list to the output stream together with error type
1243 and test case description.
1244
1245 :param flavour: error type
1246 :param errors: iterable errors
1247
1248 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001249 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001250 self.stream.writeln(double_line_delim)
1251 self.stream.writeln("%s: %s" %
1252 (flavour, self.getDescription(test)))
1253 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001254 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001255
1256
Damjan Marionf56b77a2016-10-03 19:44:57 +02001257class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001258 """
Klement Sekera104543f2017-02-03 07:29:43 +01001259 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001260 """
1261 @property
1262 def resultclass(self):
1263 """Class maintaining the results of the tests"""
1264 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001265
juraj.linkes184870a2018-07-16 14:22:01 +02001266 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001267 result_pipe=None, failfast=False, buffer=False,
juraj.linkesabec0122018-11-16 17:28:56 +01001268 resultclass=None, print_summary=True):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001269
Klement Sekera7a161da2017-01-17 13:42:48 +01001270 # ignore stream setting here, use hard-coded stdout to be in sync
1271 # with prints from VppTestCase methods ...
1272 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1273 verbosity, failfast, buffer,
1274 resultclass)
juraj.linkesccfead62018-11-21 13:20:43 +01001275 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001276
juraj.linkesabec0122018-11-16 17:28:56 +01001277 self.orig_stream = self.stream
1278 self.resultclass.test_framework_result_pipe = result_pipe
1279
1280 self.print_summary = print_summary
1281
1282 def _makeResult(self):
1283 return self.resultclass(self.stream,
1284 self.descriptions,
1285 self.verbosity,
1286 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001287
Damjan Marionf56b77a2016-10-03 19:44:57 +02001288 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001289 """
1290 Run the tests
1291
1292 :param test:
1293
1294 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001295 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001296
1297 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001298 if not self.print_summary:
1299 self.stream = self.orig_stream
1300 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001301 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001302
1303
1304class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001305 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001306 self.logger = logger
1307 self.args = args
1308 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001309 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001310 super(Worker, self).__init__()
1311
1312 def run(self):
1313 executable = self.args[0]
1314 self.logger.debug("Running executable w/args `%s'" % self.args)
1315 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001316 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001317 env["CK_LOG_FILE_NAME"] = "-"
1318 self.process = subprocess.Popen(
1319 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1320 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1321 out, err = self.process.communicate()
1322 self.logger.debug("Finished running `%s'" % executable)
1323 self.logger.info("Return code is `%s'" % self.process.returncode)
1324 self.logger.info(single_line_delim)
1325 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1326 self.logger.info(single_line_delim)
1327 self.logger.info(out)
1328 self.logger.info(single_line_delim)
1329 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1330 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001331 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001332 self.logger.info(single_line_delim)
1333 self.result = self.process.returncode