blob: 8a1bfcb660b09c564c1f0ea890992872486fdf69 [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
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100196 extra_vpp_punt_config = []
197
Klement Sekeraf62ae122016-10-11 11:47:09 +0200198 @property
199 def packet_infos(self):
200 """List of packet infos"""
201 return self._packet_infos
202
Klement Sekeradab231a2016-12-21 08:50:14 +0100203 @classmethod
204 def get_packet_count_for_if_idx(cls, dst_if_index):
205 """Get the number of packet info for specified destination if index"""
206 if dst_if_index in cls._packet_count_for_dst_if_idx:
207 return cls._packet_count_for_dst_if_idx[dst_if_index]
208 else:
209 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200210
211 @classmethod
212 def instance(cls):
213 """Return the instance of this testcase"""
214 return cls.test_instance
215
Damjan Marionf56b77a2016-10-03 19:44:57 +0200216 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200217 def set_debug_flags(cls, d):
218 cls.debug_core = False
219 cls.debug_gdb = False
220 cls.debug_gdbserver = False
221 if d is None:
222 return
223 dl = d.lower()
224 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200225 cls.debug_core = True
226 elif dl == "gdb":
227 cls.debug_gdb = True
228 elif dl == "gdbserver":
229 cls.debug_gdbserver = True
230 else:
231 raise Exception("Unrecognized DEBUG option: '%s'" % d)
232
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800233 @staticmethod
234 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200235 cpu_usage_list = [set(range(psutil.cpu_count()))]
236 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
237 if 'vpp_main' == p.info['name']]
238 for vpp_process in vpp_processes:
239 for cpu_usage_set in cpu_usage_list:
240 try:
241 cpu_num = vpp_process.cpu_num()
242 if cpu_num in cpu_usage_set:
243 cpu_usage_set_index = cpu_usage_list.index(
244 cpu_usage_set)
245 if cpu_usage_set_index == len(cpu_usage_list) - 1:
246 cpu_usage_list.append({cpu_num})
247 else:
248 cpu_usage_list[cpu_usage_set_index + 1].add(
249 cpu_num)
250 cpu_usage_set.remove(cpu_num)
251 break
252 except psutil.NoSuchProcess:
253 pass
254
255 for cpu_usage_set in cpu_usage_list:
256 if len(cpu_usage_set) > 0:
257 min_usage_set = cpu_usage_set
258 break
259
260 return random.choice(tuple(min_usage_set))
261
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800262 @classmethod
juraj.linkes40dd73b2018-09-21 13:55:16 +0200263 def print_header(cls):
264 if not hasattr(cls, '_header_printed'):
265 print(double_line_delim)
266 print(colorize(getdoc(cls).splitlines()[0], GREEN))
267 print(double_line_delim)
268 cls._header_printed = True
269
juraj.linkes184870a2018-07-16 14:22:01 +0200270 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200271 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200272 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100273 s = os.getenv("STEP", "n")
274 cls.step = True if s.lower() in ("y", "yes", "1") else False
275 d = os.getenv("DEBUG", None)
276 c = os.getenv("CACHE_OUTPUT", "1")
277 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200278 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100279 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
280 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100281 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
282 plugin_path = None
283 if cls.plugin_path is not None:
284 if cls.extern_plugin_path is not None:
285 plugin_path = "%s:%s" % (
286 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100287 else:
288 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100289 elif cls.extern_plugin_path is not None:
290 plugin_path = cls.extern_plugin_path
Klement Sekera01bbbe92016-11-02 09:25:05 +0100291 debug_cli = ""
292 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
293 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100294 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100295 size = os.getenv("COREDUMP_SIZE")
296 if size is not None:
297 coredump_size = "coredump-size %s" % size
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100298 if coredump_size is None:
Dave Wallacee2efd122017-09-30 22:04:21 -0400299 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200300
301 cpu_core_number = cls.get_least_used_cpu()
302
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100303 cls.vpp_cmdline = [cls.vpp_bin, "unix",
Dave Wallacee2efd122017-09-30 22:04:21 -0400304 "{", "nodaemon", debug_cli, "full-coredump",
Jakub Grajciar99743912018-10-09 12:28:21 +0200305 coredump_size, "runtime-dir", cls.tempdir, "}",
306 "api-trace", "{", "on", "}", "api-segment", "{",
307 "prefix", cls.shm_prefix, "}", "cpu", "{",
308 "main-core", str(cpu_core_number), "}", "statseg",
309 "{", "socket-name", cls.stats_sock, "}", "plugins",
310 "{", "plugin", "dpdk_plugin.so", "{", "disable",
311 "}", "plugin", "unittest_plugin.so", "{", "enable",
312 "}", "}", ]
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100313 if cls.extra_vpp_punt_config is not None:
314 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100315 if plugin_path is not None:
316 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100317 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
318 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200319
320 @classmethod
321 def wait_for_enter(cls):
322 if cls.debug_gdbserver:
323 print(double_line_delim)
324 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
325 elif cls.debug_gdb:
326 print(double_line_delim)
327 print("Spawned VPP with PID: %d" % cls.vpp.pid)
328 else:
329 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
330 return
331 print(single_line_delim)
332 print("You can debug the VPP using e.g.:")
333 if cls.debug_gdbserver:
334 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
335 print("Now is the time to attach a gdb by running the above "
336 "command, set up breakpoints etc. and then resume VPP from "
337 "within gdb by issuing the 'continue' command")
338 elif cls.debug_gdb:
339 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
340 print("Now is the time to attach a gdb by running the above "
341 "command and set up breakpoints etc.")
342 print(single_line_delim)
Ole Trøan162989e2018-11-26 10:27:50 +0000343 raw_input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200344
345 @classmethod
346 def run_vpp(cls):
347 cmdline = cls.vpp_cmdline
348
349 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100350 gdbserver = '/usr/bin/gdbserver'
351 if not os.path.isfile(gdbserver) or \
352 not os.access(gdbserver, os.X_OK):
353 raise Exception("gdbserver binary '%s' does not exist or is "
354 "not executable" % gdbserver)
355
356 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200357 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
358
Klement Sekera931be3a2016-11-03 05:36:01 +0100359 try:
360 cls.vpp = subprocess.Popen(cmdline,
361 stdout=subprocess.PIPE,
362 stderr=subprocess.PIPE,
363 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800364 except subprocess.CalledProcessError as e:
Klement Sekera931be3a2016-11-03 05:36:01 +0100365 cls.logger.critical("Couldn't start vpp: %s" % e)
366 raise
367
Klement Sekera277b89c2016-10-28 13:20:27 +0200368 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100369
Damjan Marionf56b77a2016-10-03 19:44:57 +0200370 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200371 def wait_for_stats_socket(cls):
372 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800373 ok = False
374 while time.time() < deadline or \
375 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200376 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800377 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200378 break
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800379 time.sleep(0.8)
380 if not ok:
381 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200382
383 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200384 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200385 """
386 Perform class setup before running the testcase
387 Remove shared memory files, start vpp and connect the vpp-api
388 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100389 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100390 random.seed()
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800391 cls.print_header()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100392 cls.logger = get_logger(cls.__name__)
393 if hasattr(cls, 'parallel_handler'):
394 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100395 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200396 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200397 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200398 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200399 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
400 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100401 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
402 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200403 cls.file_handler.setLevel(DEBUG)
404 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200405 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200406 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200407 cls.logger.info("Temporary dir is %s, shm prefix is %s",
408 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200409 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100410 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100411 cls._captures = []
412 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200413 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100414 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100415 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200416 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200417 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200418 # need to catch exceptions here because if we raise, then the cleanup
419 # doesn't get called and we might end with a zombie vpp
420 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200421 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200422 cls.reporter.send_keep_alive(cls, 'setUpClass')
423 VppTestResult.current_test_case_info = TestCaseInfo(
424 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100425 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100426 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100427 cls.pump_thread_stop_flag = Event()
428 cls.pump_thread_wakeup_pipe = os.pipe()
429 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100430 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100431 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200432 if cls.debug_gdb or cls.debug_gdbserver:
433 read_timeout = 0
434 else:
435 read_timeout = 5
436 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
437 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100438 if cls.step:
439 hook = StepHook(cls)
440 else:
441 hook = PollHook(cls)
442 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200443 cls.wait_for_stats_socket()
444 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200445 try:
446 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100447 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200448 cls.vpp_startup_failed = True
449 cls.logger.critical(
450 "VPP died shortly after startup, check the"
451 " output to standard error for possible cause")
452 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100453 try:
454 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100455 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100456 try:
457 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100458 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100459 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100460 if cls.debug_gdbserver:
461 print(colorize("You're running VPP inside gdbserver but "
462 "VPP-API connection failed, did you forget "
463 "to 'continue' VPP from within gdb?", RED))
464 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100465 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100466 try:
467 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100468 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100469 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100470 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200471
Damjan Marionf56b77a2016-10-03 19:44:57 +0200472 @classmethod
473 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200474 """
475 Disconnect vpp-api, kill vpp and cleanup shared memory files
476 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200477 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
478 cls.vpp.poll()
479 if cls.vpp.returncode is None:
480 print(double_line_delim)
481 print("VPP or GDB server is still running")
482 print(single_line_delim)
Ole Trøan162989e2018-11-26 10:27:50 +0000483 raw_input("When done debugging, press ENTER to kill the "
Klement Sekerae1ace192018-03-23 21:54:12 +0100484 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200485
juraj.linkes184870a2018-07-16 14:22:01 +0200486 # first signal that we want to stop the pump thread, then wake it up
487 if hasattr(cls, 'pump_thread_stop_flag'):
488 cls.pump_thread_stop_flag.set()
489 if hasattr(cls, 'pump_thread_wakeup_pipe'):
490 os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100491 if hasattr(cls, 'pump_thread'):
492 cls.logger.debug("Waiting for pump thread to stop")
493 cls.pump_thread.join()
494 if hasattr(cls, 'vpp_stderr_reader_thread'):
495 cls.logger.debug("Waiting for stdderr pump to stop")
496 cls.vpp_stderr_reader_thread.join()
497
Klement Sekeraf62ae122016-10-11 11:47:09 +0200498 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100499 if hasattr(cls, 'vapi'):
500 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100501 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200502 cls.vpp.poll()
503 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100504 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200505 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100506 cls.logger.debug("Waiting for vpp to die")
507 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200508 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200509
Klement Sekera3747c752017-04-10 06:30:17 +0200510 if cls.vpp_startup_failed:
511 stdout_log = cls.logger.info
512 stderr_log = cls.logger.critical
513 else:
514 stdout_log = cls.logger.info
515 stderr_log = cls.logger.info
516
Klement Sekerae4504c62016-12-08 10:16:41 +0100517 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200518 stdout_log(single_line_delim)
519 stdout_log('VPP output to stdout while running %s:', cls.__name__)
520 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100521 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200522 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
523 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200524 stdout_log('\n%s', vpp_output)
525 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200526
Klement Sekerae4504c62016-12-08 10:16:41 +0100527 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200528 stderr_log(single_line_delim)
529 stderr_log('VPP output to stderr while running %s:', cls.__name__)
530 stderr_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100531 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200532 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
533 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200534 stderr_log('\n%s', vpp_output)
535 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200536
Damjan Marionf56b77a2016-10-03 19:44:57 +0200537 @classmethod
538 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200539 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200540 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200541 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200542 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100543 cls.reset_packet_infos()
544 if debug_framework:
545 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200546
Damjan Marionf56b77a2016-10-03 19:44:57 +0200547 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200548 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100549 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
550 (self.__class__.__name__, self._testMethodName,
551 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200552 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200553 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700554 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200555 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200556 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200557 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800558 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100559 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500560 # Save/Dump VPP api trace log
561 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
562 tmp_api_trace = "/tmp/%s" % api_trace
563 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
564 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
565 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
566 vpp_api_trace_log))
567 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500568 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500569 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100570 else:
571 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200572
Damjan Marionf56b77a2016-10-03 19:44:57 +0200573 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200574 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200575 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100576 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
577 (self.__class__.__name__, self._testMethodName,
578 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100579 if self.vpp_dead:
580 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100581 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100582 self.vpp_stdout_deque.append(
583 "--- test setUp() for %s.%s(%s) starts here ---\n" %
584 (self.__class__.__name__, self._testMethodName,
585 self._testMethodDoc))
586 self.vpp_stderr_deque.append(
587 "--- test setUp() for %s.%s(%s) starts here ---\n" %
588 (self.__class__.__name__, self._testMethodName,
589 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200590 self.vapi.cli("clear trace")
591 # store the test instance inside the test class - so that objects
592 # holding the class can access instance methods (like assertEqual)
593 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200594
Damjan Marionf56b77a2016-10-03 19:44:57 +0200595 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200596 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200597 """
598 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200599
Klement Sekera75e7d132017-09-20 08:26:30 +0200600 :param interfaces: iterable interface indexes (if None,
601 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200602
Klement Sekeraf62ae122016-10-11 11:47:09 +0200603 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200604 if interfaces is None:
605 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200606 for i in interfaces:
607 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200608
Damjan Marionf56b77a2016-10-03 19:44:57 +0200609 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100610 def register_capture(cls, cap_name):
611 """ Register a capture in the testclass """
612 # add to the list of captures with current timestamp
613 cls._captures.append((time.time(), cap_name))
614 # filter out from zombies
615 cls._zombie_captures = [(stamp, name)
616 for (stamp, name) in cls._zombie_captures
617 if name != cap_name]
618
619 @classmethod
620 def pg_start(cls):
621 """ Remove any zombie captures and enable the packet generator """
622 # how long before capture is allowed to be deleted - otherwise vpp
623 # crashes - 100ms seems enough (this shouldn't be needed at all)
624 capture_ttl = 0.1
625 now = time.time()
626 for stamp, cap_name in cls._zombie_captures:
627 wait = stamp + capture_ttl - now
628 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100629 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100630 now = time.time()
631 cls.logger.debug("Removing zombie capture %s" % cap_name)
632 cls.vapi.cli('packet-generator delete %s' % cap_name)
633
Klement Sekeraf62ae122016-10-11 11:47:09 +0200634 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
635 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100636 cls._zombie_captures = cls._captures
637 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200638
Damjan Marionf56b77a2016-10-03 19:44:57 +0200639 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200640 def create_pg_interfaces(cls, interfaces):
641 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100642 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200643
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100644 :param interfaces: iterable indexes of the interfaces.
645 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200646
Klement Sekeraf62ae122016-10-11 11:47:09 +0200647 """
648 result = []
649 for i in interfaces:
650 intf = VppPGInterface(cls, i)
651 setattr(cls, intf.name, intf)
652 result.append(intf)
653 cls.pg_interfaces = result
654 return result
655
Matej Klotton0178d522016-11-04 11:11:44 +0100656 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200657 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100658 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100659 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100660
Klement Sekerab9ef2732018-06-24 22:49:33 +0200661 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100662 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100663 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200664 result = [VppLoInterface(cls) for i in range(count)]
665 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100666 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100667 cls.lo_interfaces = result
668 return result
669
Damjan Marionf56b77a2016-10-03 19:44:57 +0200670 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200671 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200672 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200673 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200674 NOTE: Currently works only when Raw layer is present.
675
676 :param packet: packet
677 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200678 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200679
680 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200681 packet_len = len(packet) + 4
682 extend = size - packet_len
683 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200684 num = (extend / len(padding)) + 1
685 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200686
Klement Sekeradab231a2016-12-21 08:50:14 +0100687 @classmethod
688 def reset_packet_infos(cls):
689 """ Reset the list of packet info objects and packet counts to zero """
690 cls._packet_infos = {}
691 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200692
Klement Sekeradab231a2016-12-21 08:50:14 +0100693 @classmethod
694 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200695 """
696 Create packet info object containing the source and destination indexes
697 and add it to the testcase's packet info list
698
Klement Sekeradab231a2016-12-21 08:50:14 +0100699 :param VppInterface src_if: source interface
700 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200701
702 :returns: _PacketInfo object
703
704 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200705 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100706 info.index = len(cls._packet_infos)
707 info.src = src_if.sw_if_index
708 info.dst = dst_if.sw_if_index
709 if isinstance(dst_if, VppSubInterface):
710 dst_idx = dst_if.parent.sw_if_index
711 else:
712 dst_idx = dst_if.sw_if_index
713 if dst_idx in cls._packet_count_for_dst_if_idx:
714 cls._packet_count_for_dst_if_idx[dst_idx] += 1
715 else:
716 cls._packet_count_for_dst_if_idx[dst_idx] = 1
717 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200718 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200719
Damjan Marionf56b77a2016-10-03 19:44:57 +0200720 @staticmethod
721 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200722 """
723 Convert _PacketInfo object to packet payload
724
725 :param info: _PacketInfo object
726
727 :returns: string containing serialized data from packet info
728 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100729 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
730 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200731
Damjan Marionf56b77a2016-10-03 19:44:57 +0200732 @staticmethod
733 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200734 """
735 Convert packet payload to _PacketInfo object
736
737 :param payload: packet payload
738
739 :returns: _PacketInfo object containing de-serialized data from payload
740
741 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200742 numbers = payload.split()
743 info = _PacketInfo()
744 info.index = int(numbers[0])
745 info.src = int(numbers[1])
746 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100747 info.ip = int(numbers[3])
748 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200749 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200750
Damjan Marionf56b77a2016-10-03 19:44:57 +0200751 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200752 """
753 Iterate over the packet info list stored in the testcase
754 Start iteration with first element if info is None
755 Continue based on index in info if info is specified
756
757 :param info: info or None
758 :returns: next info in list or None if no more infos
759 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200760 if info is None:
761 next_index = 0
762 else:
763 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100764 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200765 return None
766 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100767 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200768
Klement Sekeraf62ae122016-10-11 11:47:09 +0200769 def get_next_packet_info_for_interface(self, src_index, info):
770 """
771 Search the packet info list for the next packet info with same source
772 interface index
773
774 :param src_index: source interface index to search for
775 :param info: packet info - where to start the search
776 :returns: packet info or None
777
778 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200779 while True:
780 info = self.get_next_packet_info(info)
781 if info is None:
782 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200783 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200785
Klement Sekeraf62ae122016-10-11 11:47:09 +0200786 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
787 """
788 Search the packet info list for the next packet info with same source
789 and destination interface indexes
790
791 :param src_index: source interface index to search for
792 :param dst_index: destination interface index to search for
793 :param info: packet info - where to start the search
794 :returns: packet info or None
795
796 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200797 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200798 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200799 if info is None:
800 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200801 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200802 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200803
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200804 def assert_equal(self, real_value, expected_value, name_or_class=None):
805 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100806 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200807 return
808 try:
809 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
810 msg = msg % (getdoc(name_or_class).strip(),
811 real_value, str(name_or_class(real_value)),
812 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100813 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200814 msg = "Invalid %s: %s does not match expected value %s" % (
815 name_or_class, real_value, expected_value)
816
817 self.assertEqual(real_value, expected_value, msg)
818
Klement Sekerab17dd962017-01-09 07:43:48 +0100819 def assert_in_range(self,
820 real_value,
821 expected_min,
822 expected_max,
823 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200824 if name is None:
825 msg = None
826 else:
827 msg = "Invalid %s: %s out of range <%s,%s>" % (
828 name, real_value, expected_min, expected_max)
829 self.assertTrue(expected_min <= real_value <= expected_max, msg)
830
Klement Sekerad81ae412018-05-16 10:52:54 +0200831 def assert_packet_checksums_valid(self, packet,
832 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200833 received = packet.__class__(str(packet))
834 self.logger.debug(
835 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200836 udp_layers = ['UDP', 'UDPerror']
837 checksum_fields = ['cksum', 'chksum']
838 checksums = []
839 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200840 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200841 while True:
842 layer = temp.getlayer(counter)
843 if layer:
844 for cf in checksum_fields:
845 if hasattr(layer, cf):
846 if ignore_zero_udp_checksums and \
847 0 == getattr(layer, cf) and \
848 layer.name in udp_layers:
849 continue
850 delattr(layer, cf)
851 checksums.append((counter, cf))
852 else:
853 break
854 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200855 if 0 == len(checksums):
856 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200857 temp = temp.__class__(str(temp))
858 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200859 calc_sum = getattr(temp[layer], cf)
860 self.assert_equal(
861 getattr(received[layer], cf), calc_sum,
862 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
863 self.logger.debug(
864 "Checksum field `%s` on `%s` layer has correct value `%s`" %
865 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200866
867 def assert_checksum_valid(self, received_packet, layer,
868 field_name='chksum',
869 ignore_zero_checksum=False):
870 """ Check checksum of received packet on given layer """
871 received_packet_checksum = getattr(received_packet[layer], field_name)
872 if ignore_zero_checksum and 0 == received_packet_checksum:
873 return
874 recalculated = received_packet.__class__(str(received_packet))
875 delattr(recalculated[layer], field_name)
876 recalculated = recalculated.__class__(str(recalculated))
877 self.assert_equal(received_packet_checksum,
878 getattr(recalculated[layer], field_name),
879 "packet checksum on layer: %s" % layer)
880
881 def assert_ip_checksum_valid(self, received_packet,
882 ignore_zero_checksum=False):
883 self.assert_checksum_valid(received_packet, 'IP',
884 ignore_zero_checksum=ignore_zero_checksum)
885
886 def assert_tcp_checksum_valid(self, received_packet,
887 ignore_zero_checksum=False):
888 self.assert_checksum_valid(received_packet, 'TCP',
889 ignore_zero_checksum=ignore_zero_checksum)
890
891 def assert_udp_checksum_valid(self, received_packet,
892 ignore_zero_checksum=True):
893 self.assert_checksum_valid(received_packet, 'UDP',
894 ignore_zero_checksum=ignore_zero_checksum)
895
896 def assert_embedded_icmp_checksum_valid(self, received_packet):
897 if received_packet.haslayer(IPerror):
898 self.assert_checksum_valid(received_packet, 'IPerror')
899 if received_packet.haslayer(TCPerror):
900 self.assert_checksum_valid(received_packet, 'TCPerror')
901 if received_packet.haslayer(UDPerror):
902 self.assert_checksum_valid(received_packet, 'UDPerror',
903 ignore_zero_checksum=True)
904 if received_packet.haslayer(ICMPerror):
905 self.assert_checksum_valid(received_packet, 'ICMPerror')
906
907 def assert_icmp_checksum_valid(self, received_packet):
908 self.assert_checksum_valid(received_packet, 'ICMP')
909 self.assert_embedded_icmp_checksum_valid(received_packet)
910
911 def assert_icmpv6_checksum_valid(self, pkt):
912 if pkt.haslayer(ICMPv6DestUnreach):
913 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
914 self.assert_embedded_icmp_checksum_valid(pkt)
915 if pkt.haslayer(ICMPv6EchoRequest):
916 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
917 if pkt.haslayer(ICMPv6EchoReply):
918 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
919
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100920 def assert_packet_counter_equal(self, counter, expected_value):
921 counters = self.vapi.cli("sh errors").split('\n')
922 counter_value = -1
923 for i in range(1, len(counters)-1):
924 results = counters[i].split()
925 if results[1] == counter:
926 counter_value = int(results[0])
927 break
928 self.assert_equal(counter_value, expected_value,
929 "packet counter `%s'" % counter)
930
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100931 @classmethod
932 def sleep(cls, timeout, remark=None):
933 if hasattr(cls, 'logger'):
Klement Sekera3cfa5582017-04-19 07:10:58 +0000934 cls.logger.debug("Starting sleep for %ss (%s)" % (timeout, remark))
935 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100936 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000937 after = time.time()
938 if after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200939 cls.logger.error("unexpected time.sleep() result - "
940 "slept for %ss instead of ~%ss!" % (
941 after - before, timeout))
Klement Sekera3cfa5582017-04-19 07:10:58 +0000942 if hasattr(cls, 'logger'):
943 cls.logger.debug(
944 "Finished sleep (%s) - slept %ss (wanted %ss)" % (
945 remark, after - before, timeout))
Klement Sekeraa57a9702017-02-02 06:58:07 +0100946
Neale Ranns947ea622018-06-07 23:48:20 -0700947 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800948 self.vapi.cli("clear trace")
949 intf.add_stream(pkts)
950 self.pg_enable_capture(self.pg_interfaces)
951 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700952 if not timeout:
953 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800954 for i in self.pg_interfaces:
955 i.get_capture(0, timeout=timeout)
956 i.assert_nothing_captured(remark=remark)
957 timeout = 0.1
958
959 def send_and_expect(self, input, pkts, output):
960 self.vapi.cli("clear trace")
961 input.add_stream(pkts)
962 self.pg_enable_capture(self.pg_interfaces)
963 self.pg_start()
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700964 if isinstance(object, (list,)):
965 rx = []
966 for o in output:
967 rx.append(output.get_capture(len(pkts)))
968 else:
969 rx = output.get_capture(len(pkts))
970 return rx
971
972 def send_and_expect_only(self, input, pkts, output, timeout=None):
973 self.vapi.cli("clear trace")
974 input.add_stream(pkts)
975 self.pg_enable_capture(self.pg_interfaces)
976 self.pg_start()
977 if isinstance(object, (list,)):
978 outputs = output
979 rx = []
980 for o in outputs:
981 rx.append(output.get_capture(len(pkts)))
982 else:
983 rx = output.get_capture(len(pkts))
984 outputs = [output]
985 if not timeout:
986 timeout = 1
987 for i in self.pg_interfaces:
988 if i not in outputs:
989 i.get_capture(0, timeout=timeout)
990 i.assert_nothing_captured()
991 timeout = 0.1
992
Neale Ranns52fae862018-01-08 04:41:42 -0800993 return rx
994
Damjan Marionf56b77a2016-10-03 19:44:57 +0200995
juraj.linkes184870a2018-07-16 14:22:01 +0200996def get_testcase_doc_name(test):
997 return getdoc(test.__class__).splitlines()[0]
998
999
Ole Trøan5ba91592018-11-22 10:01:09 +00001000def get_test_description(descriptions, test):
1001 short_description = test.shortDescription()
1002 if descriptions and short_description:
1003 return short_description
1004 else:
1005 return str(test)
1006
1007
juraj.linkes40dd73b2018-09-21 13:55:16 +02001008class TestCaseInfo(object):
1009 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1010 self.logger = logger
1011 self.tempdir = tempdir
1012 self.vpp_pid = vpp_pid
1013 self.vpp_bin_path = vpp_bin_path
1014 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001015
1016
Damjan Marionf56b77a2016-10-03 19:44:57 +02001017class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001018 """
1019 @property result_string
1020 String variable to store the test case result string.
1021 @property errors
1022 List variable containing 2-tuples of TestCase instances and strings
1023 holding formatted tracebacks. Each tuple represents a test which
1024 raised an unexpected exception.
1025 @property failures
1026 List variable containing 2-tuples of TestCase instances and strings
1027 holding formatted tracebacks. Each tuple represents a test where
1028 a failure was explicitly signalled using the TestCase.assert*()
1029 methods.
1030 """
1031
juraj.linkes40dd73b2018-09-21 13:55:16 +02001032 failed_test_cases_info = set()
1033 core_crash_test_cases_info = set()
1034 current_test_case_info = None
1035
juraj.linkesabec0122018-11-16 17:28:56 +01001036 def __init__(self, stream, descriptions, verbosity, runner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001037 """
Klement Sekerada505f62017-01-04 12:58:53 +01001038 :param stream File descriptor to store where to report test results.
1039 Set to the standard error stream by default.
1040 :param descriptions Boolean variable to store information if to use
1041 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001042 :param verbosity Integer variable to store required verbosity level.
1043 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001044 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
1045 self.stream = stream
1046 self.descriptions = descriptions
1047 self.verbosity = verbosity
1048 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001049 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001050
Damjan Marionf56b77a2016-10-03 19:44:57 +02001051 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001052 """
1053 Record a test succeeded result
1054
1055 :param test:
1056
1057 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001058 if self.current_test_case_info:
1059 self.current_test_case_info.logger.debug(
1060 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1061 test._testMethodName,
1062 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001063 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001064 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001065
juraj.linkescae64f82018-09-19 15:01:47 +02001066 self.send_result_through_pipe(test, PASS)
1067
Klement Sekeraf62ae122016-10-11 11:47:09 +02001068 def addSkip(self, test, reason):
1069 """
1070 Record a test skipped.
1071
1072 :param test:
1073 :param reason:
1074
1075 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001076 if self.current_test_case_info:
1077 self.current_test_case_info.logger.debug(
1078 "--- addSkip() %s.%s(%s) called, reason is %s" %
1079 (test.__class__.__name__, test._testMethodName,
1080 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001081 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001082 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001083
juraj.linkescae64f82018-09-19 15:01:47 +02001084 self.send_result_through_pipe(test, SKIP)
1085
juraj.linkes40dd73b2018-09-21 13:55:16 +02001086 def symlink_failed(self):
1087 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001088 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001089 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001090 link_path = os.path.join(
1091 failed_dir,
1092 '%s-FAILED' %
1093 os.path.basename(self.current_test_case_info.tempdir))
1094 if self.current_test_case_info.logger:
1095 self.current_test_case_info.logger.debug(
1096 "creating a link to the failed test")
1097 self.current_test_case_info.logger.debug(
1098 "os.symlink(%s, %s)" %
1099 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001100 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001101 if self.current_test_case_info.logger:
1102 self.current_test_case_info.logger.debug(
1103 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001104 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001105 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001106
Klement Sekeraf413bef2017-08-15 07:09:02 +02001107 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001108 if self.current_test_case_info.logger:
1109 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001110
juraj.linkescae64f82018-09-19 15:01:47 +02001111 def send_result_through_pipe(self, test, result):
1112 if hasattr(self, 'test_framework_result_pipe'):
1113 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001114 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001115 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001116
juraj.linkes40dd73b2018-09-21 13:55:16 +02001117 def log_error(self, test, err, fn_name):
1118 if self.current_test_case_info:
1119 if isinstance(test, unittest.suite._ErrorHolder):
1120 test_name = test.description
1121 else:
1122 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1123 test._testMethodName,
1124 test._testMethodDoc)
1125 self.current_test_case_info.logger.debug(
1126 "--- %s() %s called, err is %s" %
1127 (fn_name, test_name, err))
1128 self.current_test_case_info.logger.debug(
1129 "formatted exception is:\n%s" %
1130 "".join(format_exception(*err)))
1131
1132 def add_error(self, test, err, unittest_fn, error_type):
1133 if error_type == FAIL:
1134 self.log_error(test, err, 'addFailure')
1135 error_type_str = colorize("FAIL", RED)
1136 elif error_type == ERROR:
1137 self.log_error(test, err, 'addError')
1138 error_type_str = colorize("ERROR", RED)
1139 else:
1140 raise Exception('Error type %s cannot be used to record an '
1141 'error or a failure' % error_type)
1142
1143 unittest_fn(self, test, err)
1144 if self.current_test_case_info:
1145 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1146 (error_type_str,
1147 self.current_test_case_info.tempdir)
1148 self.symlink_failed()
1149 self.failed_test_cases_info.add(self.current_test_case_info)
1150 if is_core_present(self.current_test_case_info.tempdir):
1151 if not self.current_test_case_info.core_crash_test:
1152 if isinstance(test, unittest.suite._ErrorHolder):
1153 test_name = str(test)
1154 else:
1155 test_name = "'{}' ({})".format(
1156 get_testcase_doc_name(test), test.id())
1157 self.current_test_case_info.core_crash_test = test_name
1158 self.core_crash_test_cases_info.add(
1159 self.current_test_case_info)
1160 else:
1161 self.result_string = '%s [no temp dir]' % error_type_str
1162
1163 self.send_result_through_pipe(test, error_type)
1164
Damjan Marionf56b77a2016-10-03 19:44:57 +02001165 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001166 """
1167 Record a test failed result
1168
1169 :param test:
1170 :param err: error message
1171
1172 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001173 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001174
Damjan Marionf56b77a2016-10-03 19:44:57 +02001175 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001176 """
1177 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001178
Klement Sekeraf62ae122016-10-11 11:47:09 +02001179 :param test:
1180 :param err: error message
1181
1182 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001183 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001184
Damjan Marionf56b77a2016-10-03 19:44:57 +02001185 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001186 """
1187 Get test description
1188
1189 :param test:
1190 :returns: test description
1191
1192 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001193 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001194
Damjan Marionf56b77a2016-10-03 19:44:57 +02001195 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001196 """
1197 Start a test
1198
1199 :param test:
1200
1201 """
Paul Vinciguerra86ebba62018-11-21 09:28:32 -08001202 test.print_header()
juraj.linkes40dd73b2018-09-21 13:55:16 +02001203
Damjan Marionf56b77a2016-10-03 19:44:57 +02001204 unittest.TestResult.startTest(self, test)
1205 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001206 self.stream.writeln(
1207 "Starting " + self.getDescription(test) + " ...")
1208 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001209
Damjan Marionf56b77a2016-10-03 19:44:57 +02001210 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001211 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001212 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001213
1214 :param test:
1215
1216 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001217 unittest.TestResult.stopTest(self, test)
1218 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001219 self.stream.writeln(single_line_delim)
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))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001222 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001223 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001224 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001225 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001226
1227 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001228
Damjan Marionf56b77a2016-10-03 19:44:57 +02001229 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001230 """
1231 Print errors from running the test case
1232 """
juraj.linkesabec0122018-11-16 17:28:56 +01001233 if len(self.errors) > 0 or len(self.failures) > 0:
1234 self.stream.writeln()
1235 self.printErrorList('ERROR', self.errors)
1236 self.printErrorList('FAIL', self.failures)
1237
1238 # ^^ that is the last output from unittest before summary
1239 if not self.runner.print_summary:
1240 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1241 self.stream = devnull
1242 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001243
Damjan Marionf56b77a2016-10-03 19:44:57 +02001244 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001245 """
1246 Print error list to the output stream together with error type
1247 and test case description.
1248
1249 :param flavour: error type
1250 :param errors: iterable errors
1251
1252 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001253 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001254 self.stream.writeln(double_line_delim)
1255 self.stream.writeln("%s: %s" %
1256 (flavour, self.getDescription(test)))
1257 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001258 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001259
1260
Damjan Marionf56b77a2016-10-03 19:44:57 +02001261class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001262 """
Klement Sekera104543f2017-02-03 07:29:43 +01001263 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001264 """
1265 @property
1266 def resultclass(self):
1267 """Class maintaining the results of the tests"""
1268 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001269
juraj.linkes184870a2018-07-16 14:22:01 +02001270 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001271 result_pipe=None, failfast=False, buffer=False,
juraj.linkesabec0122018-11-16 17:28:56 +01001272 resultclass=None, print_summary=True):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001273
Klement Sekera7a161da2017-01-17 13:42:48 +01001274 # ignore stream setting here, use hard-coded stdout to be in sync
1275 # with prints from VppTestCase methods ...
1276 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1277 verbosity, failfast, buffer,
1278 resultclass)
juraj.linkesccfead62018-11-21 13:20:43 +01001279 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001280
juraj.linkesabec0122018-11-16 17:28:56 +01001281 self.orig_stream = self.stream
1282 self.resultclass.test_framework_result_pipe = result_pipe
1283
1284 self.print_summary = print_summary
1285
1286 def _makeResult(self):
1287 return self.resultclass(self.stream,
1288 self.descriptions,
1289 self.verbosity,
1290 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001291
Damjan Marionf56b77a2016-10-03 19:44:57 +02001292 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001293 """
1294 Run the tests
1295
1296 :param test:
1297
1298 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001299 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001300
1301 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001302 if not self.print_summary:
1303 self.stream = self.orig_stream
1304 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001305 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001306
1307
1308class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001309 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001310 self.logger = logger
1311 self.args = args
1312 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001313 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001314 super(Worker, self).__init__()
1315
1316 def run(self):
1317 executable = self.args[0]
1318 self.logger.debug("Running executable w/args `%s'" % self.args)
1319 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001320 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001321 env["CK_LOG_FILE_NAME"] = "-"
1322 self.process = subprocess.Popen(
1323 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1324 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1325 out, err = self.process.communicate()
1326 self.logger.debug("Finished running `%s'" % executable)
1327 self.logger.info("Return code is `%s'" % self.process.returncode)
1328 self.logger.info(single_line_delim)
1329 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1330 self.logger.info(single_line_delim)
1331 self.logger.info(out)
1332 self.logger.info(single_line_delim)
1333 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1334 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001335 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001336 self.logger.info(single_line_delim)
1337 self.result = self.process.returncode