blob: 4c536e062b09448a5b28bc4b6654494e6023ec0a [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
5import sys
6import os
7import select
Damjan Marionf56b77a2016-10-03 19:44:57 +02008import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +02009import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020010import time
Klement Sekera3658adc2017-06-07 08:19:47 +020011import faulthandler
Klement Sekera6a6f4f72017-11-09 09:16:39 +010012import random
Dave Wallace42996c02018-02-26 14:40:13 -050013import copy
juraj.linkes184870a2018-07-16 14:22:01 +020014import psutil
Klement Sekerae4504c62016-12-08 10:16:41 +010015from collections import deque
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010016from threading import Thread, Event
Klement Sekera909a6a12017-08-08 04:33:53 +020017from inspect import getdoc, isclass
Klement Sekerab91017a2017-02-09 06:04:36 +010018from traceback import format_exception
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010019from logging import FileHandler, DEBUG, Formatter
20from scapy.packet import Raw
Klement Sekera13a83ef2018-03-21 12:35:51 +010021from hook import StepHook, PollHook, VppDiedError
Klement Sekeraf62ae122016-10-11 11:47:09 +020022from vpp_pg_interface import VppPGInterface
Klement Sekeradab231a2016-12-21 08:50:14 +010023from vpp_sub_interface import VppSubInterface
Matej Klotton0178d522016-11-04 11:11:44 +010024from vpp_lo_interface import VppLoInterface
Klement Sekeraf62ae122016-10-11 11:47:09 +020025from vpp_papi_provider import VppPapiProvider
Ole Troan73202102018-08-31 00:29:48 +020026from vpp_papi.vpp_stats import VPPStats
Klement Sekera05742262018-03-14 18:14:49 +010027from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
juraj.linkesdfb5f2a2018-11-09 11:58:54 +010028 get_logger, colorize
Klement Sekera10db26f2017-01-11 08:16:53 +010029from vpp_object import VppObjectRegistry
juraj.linkes40dd73b2018-09-21 13:55:16 +020030from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020031from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
32from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
33from scapy.layers.inet6 import ICMPv6EchoReply
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010034if os.name == 'posix' and sys.version_info[0] < 3:
35 # using subprocess32 is recommended by python official documentation
36 # @ https://docs.python.org/2/library/subprocess.html
37 import subprocess32 as subprocess
38else:
39 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020040
Klement Sekerad81ae412018-05-16 10:52:54 +020041
juraj.linkescae64f82018-09-19 15:01:47 +020042PASS = 0
43FAIL = 1
44ERROR = 2
45SKIP = 3
46TEST_RUN = 4
47
48
Klement Sekeraebbaf552018-02-17 13:41:33 +010049debug_framework = False
50if os.getenv('TEST_DEBUG', "0") == "1":
51 debug_framework = True
52 import debug_internal
53
54
Klement Sekeraf62ae122016-10-11 11:47:09 +020055"""
56 Test framework module.
57
58 The module provides a set of tools for constructing and running tests and
59 representing the results.
60"""
61
Klement Sekeraf62ae122016-10-11 11:47:09 +020062
Damjan Marionf56b77a2016-10-03 19:44:57 +020063class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020064 """Private class to create packet info object.
65
66 Help process information about the next packet.
67 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020068 """
Matej Klotton86d87c42016-11-11 11:38:55 +010069 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020070 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010071 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020072 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010073 #: Store the index of the destination packet generator interface
74 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020075 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010076 #: Store expected ip version
77 ip = -1
78 #: Store expected upper protocol
79 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010080 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020081 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020082
Matej Klotton16a14cd2016-12-07 15:09:13 +010083 def __eq__(self, other):
84 index = self.index == other.index
85 src = self.src == other.src
86 dst = self.dst == other.dst
87 data = self.data == other.data
88 return index and src and dst and data
89
Klement Sekeraf62ae122016-10-11 11:47:09 +020090
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010091def pump_output(testclass):
92 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +010093 stdout_fragment = ""
94 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -040095 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010096 readable = select.select([testclass.vpp.stdout.fileno(),
97 testclass.vpp.stderr.fileno(),
98 testclass.pump_thread_wakeup_pipe[0]],
99 [], [])[0]
100 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100101 read = os.read(testclass.vpp.stdout.fileno(), 102400)
102 if len(read) > 0:
103 split = read.splitlines(True)
104 if len(stdout_fragment) > 0:
105 split[0] = "%s%s" % (stdout_fragment, split[0])
106 if len(split) > 0 and split[-1].endswith("\n"):
107 limit = None
108 else:
109 limit = -1
110 stdout_fragment = split[-1]
111 testclass.vpp_stdout_deque.extend(split[:limit])
112 if not testclass.cache_vpp_output:
113 for line in split[:limit]:
114 testclass.logger.debug(
115 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100116 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100117 read = os.read(testclass.vpp.stderr.fileno(), 102400)
118 if len(read) > 0:
119 split = read.splitlines(True)
120 if len(stderr_fragment) > 0:
121 split[0] = "%s%s" % (stderr_fragment, split[0])
122 if len(split) > 0 and split[-1].endswith("\n"):
123 limit = None
124 else:
125 limit = -1
126 stderr_fragment = split[-1]
127 testclass.vpp_stderr_deque.extend(split[:limit])
128 if not testclass.cache_vpp_output:
129 for line in split[:limit]:
130 testclass.logger.debug(
131 "VPP STDERR: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100132 # ignoring the dummy pipe here intentionally - the flag will take care
133 # of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200134
135
Klement Sekera87134932017-03-07 11:39:27 +0100136def running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100137 s = os.getenv("EXTENDED_TESTS", "n")
138 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100139
140
Klement Sekerad3e671e2017-09-29 12:36:37 +0200141def running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100142 os_id = os.getenv("OS_ID", "")
143 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200144
145
Klement Sekera909a6a12017-08-08 04:33:53 +0200146class KeepAliveReporter(object):
147 """
148 Singleton object which reports test start to parent process
149 """
150 _shared_state = {}
151
152 def __init__(self):
153 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800154 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200155
156 @property
157 def pipe(self):
158 return self._pipe
159
160 @pipe.setter
161 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800162 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200163 raise Exception("Internal error - pipe should only be set once.")
164 self._pipe = pipe
165
juraj.linkes40dd73b2018-09-21 13:55:16 +0200166 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200167 """
168 Write current test tmpdir & desc to keep-alive pipe to signal liveness
169 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200170 if self.pipe is None:
171 # if not running forked..
172 return
173
Klement Sekera909a6a12017-08-08 04:33:53 +0200174 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200175 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200176 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200177 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200178
Dave Wallacee2efd122017-09-30 22:04:21 -0400179 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200180
181
Damjan Marionf56b77a2016-10-03 19:44:57 +0200182class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100183 """This subclass is a base class for VPP test cases that are implemented as
184 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200185 """
186
187 @property
188 def packet_infos(self):
189 """List of packet infos"""
190 return self._packet_infos
191
Klement Sekeradab231a2016-12-21 08:50:14 +0100192 @classmethod
193 def get_packet_count_for_if_idx(cls, dst_if_index):
194 """Get the number of packet info for specified destination if index"""
195 if dst_if_index in cls._packet_count_for_dst_if_idx:
196 return cls._packet_count_for_dst_if_idx[dst_if_index]
197 else:
198 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200199
200 @classmethod
201 def instance(cls):
202 """Return the instance of this testcase"""
203 return cls.test_instance
204
Damjan Marionf56b77a2016-10-03 19:44:57 +0200205 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200206 def set_debug_flags(cls, d):
207 cls.debug_core = False
208 cls.debug_gdb = False
209 cls.debug_gdbserver = False
210 if d is None:
211 return
212 dl = d.lower()
213 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200214 cls.debug_core = True
215 elif dl == "gdb":
216 cls.debug_gdb = True
217 elif dl == "gdbserver":
218 cls.debug_gdbserver = True
219 else:
220 raise Exception("Unrecognized DEBUG option: '%s'" % d)
221
222 @classmethod
juraj.linkes184870a2018-07-16 14:22:01 +0200223 def get_least_used_cpu(self):
224 cpu_usage_list = [set(range(psutil.cpu_count()))]
225 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
226 if 'vpp_main' == p.info['name']]
227 for vpp_process in vpp_processes:
228 for cpu_usage_set in cpu_usage_list:
229 try:
230 cpu_num = vpp_process.cpu_num()
231 if cpu_num in cpu_usage_set:
232 cpu_usage_set_index = cpu_usage_list.index(
233 cpu_usage_set)
234 if cpu_usage_set_index == len(cpu_usage_list) - 1:
235 cpu_usage_list.append({cpu_num})
236 else:
237 cpu_usage_list[cpu_usage_set_index + 1].add(
238 cpu_num)
239 cpu_usage_set.remove(cpu_num)
240 break
241 except psutil.NoSuchProcess:
242 pass
243
244 for cpu_usage_set in cpu_usage_list:
245 if len(cpu_usage_set) > 0:
246 min_usage_set = cpu_usage_set
247 break
248
249 return random.choice(tuple(min_usage_set))
250
juraj.linkes40dd73b2018-09-21 13:55:16 +0200251 @staticmethod
252 def print_header(cls):
253 if not hasattr(cls, '_header_printed'):
254 print(double_line_delim)
255 print(colorize(getdoc(cls).splitlines()[0], GREEN))
256 print(double_line_delim)
257 cls._header_printed = True
258
juraj.linkes184870a2018-07-16 14:22:01 +0200259 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200260 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200261 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100262 s = os.getenv("STEP", "n")
263 cls.step = True if s.lower() in ("y", "yes", "1") else False
264 d = os.getenv("DEBUG", None)
265 c = os.getenv("CACHE_OUTPUT", "1")
266 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200267 cls.set_debug_flags(d)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200268 cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp")
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100269 cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100270 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
271 plugin_path = None
272 if cls.plugin_path is not None:
273 if cls.extern_plugin_path is not None:
274 plugin_path = "%s:%s" % (
275 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100276 else:
277 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100278 elif cls.extern_plugin_path is not None:
279 plugin_path = cls.extern_plugin_path
Klement Sekera01bbbe92016-11-02 09:25:05 +0100280 debug_cli = ""
281 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
282 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100283 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100284 size = os.getenv("COREDUMP_SIZE")
285 if size is not None:
286 coredump_size = "coredump-size %s" % size
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100287 if coredump_size is None:
Dave Wallacee2efd122017-09-30 22:04:21 -0400288 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200289
290 cpu_core_number = cls.get_least_used_cpu()
291
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100292 cls.vpp_cmdline = [cls.vpp_bin, "unix",
Dave Wallacee2efd122017-09-30 22:04:21 -0400293 "{", "nodaemon", debug_cli, "full-coredump",
Jakub Grajciar99743912018-10-09 12:28:21 +0200294 coredump_size, "runtime-dir", cls.tempdir, "}",
295 "api-trace", "{", "on", "}", "api-segment", "{",
296 "prefix", cls.shm_prefix, "}", "cpu", "{",
297 "main-core", str(cpu_core_number), "}", "statseg",
298 "{", "socket-name", cls.stats_sock, "}", "plugins",
299 "{", "plugin", "dpdk_plugin.so", "{", "disable",
300 "}", "plugin", "unittest_plugin.so", "{", "enable",
301 "}", "}", ]
Klement Sekera47e275b2017-03-21 08:21:25 +0100302 if plugin_path is not None:
303 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100304 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
305 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200306
307 @classmethod
308 def wait_for_enter(cls):
309 if cls.debug_gdbserver:
310 print(double_line_delim)
311 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
312 elif cls.debug_gdb:
313 print(double_line_delim)
314 print("Spawned VPP with PID: %d" % cls.vpp.pid)
315 else:
316 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
317 return
318 print(single_line_delim)
319 print("You can debug the VPP using e.g.:")
320 if cls.debug_gdbserver:
321 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
322 print("Now is the time to attach a gdb by running the above "
323 "command, set up breakpoints etc. and then resume VPP from "
324 "within gdb by issuing the 'continue' command")
325 elif cls.debug_gdb:
326 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
327 print("Now is the time to attach a gdb by running the above "
328 "command and set up breakpoints etc.")
329 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100330 raw_input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200331
332 @classmethod
333 def run_vpp(cls):
334 cmdline = cls.vpp_cmdline
335
336 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100337 gdbserver = '/usr/bin/gdbserver'
338 if not os.path.isfile(gdbserver) or \
339 not os.access(gdbserver, os.X_OK):
340 raise Exception("gdbserver binary '%s' does not exist or is "
341 "not executable" % gdbserver)
342
343 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200344 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
345
Klement Sekera931be3a2016-11-03 05:36:01 +0100346 try:
347 cls.vpp = subprocess.Popen(cmdline,
348 stdout=subprocess.PIPE,
349 stderr=subprocess.PIPE,
350 bufsize=1)
351 except Exception as e:
352 cls.logger.critical("Couldn't start vpp: %s" % e)
353 raise
354
Klement Sekera277b89c2016-10-28 13:20:27 +0200355 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100356
Damjan Marionf56b77a2016-10-03 19:44:57 +0200357 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200358 def wait_for_stats_socket(cls):
359 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800360 ok = False
361 while time.time() < deadline or \
362 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200363 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800364 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200365 break
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800366 time.sleep(0.8)
367 if not ok:
368 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200369
370 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200371 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200372 """
373 Perform class setup before running the testcase
374 Remove shared memory files, start vpp and connect the vpp-api
375 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100376 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100377 random.seed()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200378 cls.print_header(cls)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100379 cls.logger = get_logger(cls.__name__)
380 if hasattr(cls, 'parallel_handler'):
381 cls.logger.addHandler(cls.parallel_handler)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200382 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200383 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200384 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200385 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
386 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100387 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
388 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200389 cls.file_handler.setLevel(DEBUG)
390 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200391 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200392 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200393 cls.logger.info("Temporary dir is %s, shm prefix is %s",
394 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200395 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100396 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100397 cls._captures = []
398 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200399 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100400 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100401 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200402 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200403 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200404 # need to catch exceptions here because if we raise, then the cleanup
405 # doesn't get called and we might end with a zombie vpp
406 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200407 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200408 cls.reporter.send_keep_alive(cls, 'setUpClass')
409 VppTestResult.current_test_case_info = TestCaseInfo(
410 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100411 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100412 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100413 cls.pump_thread_stop_flag = Event()
414 cls.pump_thread_wakeup_pipe = os.pipe()
415 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100416 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100417 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200418 if cls.debug_gdb or cls.debug_gdbserver:
419 read_timeout = 0
420 else:
421 read_timeout = 5
422 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
423 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100424 if cls.step:
425 hook = StepHook(cls)
426 else:
427 hook = PollHook(cls)
428 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200429 cls.wait_for_stats_socket()
430 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200431 try:
432 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100433 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200434 cls.vpp_startup_failed = True
435 cls.logger.critical(
436 "VPP died shortly after startup, check the"
437 " output to standard error for possible cause")
438 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100439 try:
440 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100441 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100442 try:
443 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100444 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100445 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100446 if cls.debug_gdbserver:
447 print(colorize("You're running VPP inside gdbserver but "
448 "VPP-API connection failed, did you forget "
449 "to 'continue' VPP from within gdb?", RED))
450 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100451 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100452 try:
453 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100454 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100455 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100456 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200457
Damjan Marionf56b77a2016-10-03 19:44:57 +0200458 @classmethod
459 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200460 """
461 Disconnect vpp-api, kill vpp and cleanup shared memory files
462 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200463 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
464 cls.vpp.poll()
465 if cls.vpp.returncode is None:
466 print(double_line_delim)
467 print("VPP or GDB server is still running")
468 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100469 raw_input("When done debugging, press ENTER to kill the "
470 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200471
juraj.linkes184870a2018-07-16 14:22:01 +0200472 # first signal that we want to stop the pump thread, then wake it up
473 if hasattr(cls, 'pump_thread_stop_flag'):
474 cls.pump_thread_stop_flag.set()
475 if hasattr(cls, 'pump_thread_wakeup_pipe'):
476 os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100477 if hasattr(cls, 'pump_thread'):
478 cls.logger.debug("Waiting for pump thread to stop")
479 cls.pump_thread.join()
480 if hasattr(cls, 'vpp_stderr_reader_thread'):
481 cls.logger.debug("Waiting for stdderr pump to stop")
482 cls.vpp_stderr_reader_thread.join()
483
Klement Sekeraf62ae122016-10-11 11:47:09 +0200484 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100485 if hasattr(cls, 'vapi'):
486 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100487 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200488 cls.vpp.poll()
489 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100490 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200491 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100492 cls.logger.debug("Waiting for vpp to die")
493 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200494 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200495
Klement Sekera3747c752017-04-10 06:30:17 +0200496 if cls.vpp_startup_failed:
497 stdout_log = cls.logger.info
498 stderr_log = cls.logger.critical
499 else:
500 stdout_log = cls.logger.info
501 stderr_log = cls.logger.info
502
Klement Sekerae4504c62016-12-08 10:16:41 +0100503 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200504 stdout_log(single_line_delim)
505 stdout_log('VPP output to stdout while running %s:', cls.__name__)
506 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100507 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200508 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
509 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200510 stdout_log('\n%s', vpp_output)
511 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200512
Klement Sekerae4504c62016-12-08 10:16:41 +0100513 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200514 stderr_log(single_line_delim)
515 stderr_log('VPP output to stderr while running %s:', cls.__name__)
516 stderr_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100517 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200518 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
519 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200520 stderr_log('\n%s', vpp_output)
521 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200522
Damjan Marionf56b77a2016-10-03 19:44:57 +0200523 @classmethod
524 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200525 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200526 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200527 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200528 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100529 cls.reset_packet_infos()
530 if debug_framework:
531 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200532
Damjan Marionf56b77a2016-10-03 19:44:57 +0200533 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200534 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100535 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
536 (self.__class__.__name__, self._testMethodName,
537 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200538 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200539 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700540 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200541 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200542 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200543 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800544 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100545 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500546 # Save/Dump VPP api trace log
547 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
548 tmp_api_trace = "/tmp/%s" % api_trace
549 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
550 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
551 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
552 vpp_api_trace_log))
553 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500554 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500555 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100556 else:
557 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200558
Damjan Marionf56b77a2016-10-03 19:44:57 +0200559 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200560 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200561 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100562 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
563 (self.__class__.__name__, self._testMethodName,
564 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100565 if self.vpp_dead:
566 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100567 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100568 self.vpp_stdout_deque.append(
569 "--- test setUp() for %s.%s(%s) starts here ---\n" %
570 (self.__class__.__name__, self._testMethodName,
571 self._testMethodDoc))
572 self.vpp_stderr_deque.append(
573 "--- test setUp() for %s.%s(%s) starts here ---\n" %
574 (self.__class__.__name__, self._testMethodName,
575 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200576 self.vapi.cli("clear trace")
577 # store the test instance inside the test class - so that objects
578 # holding the class can access instance methods (like assertEqual)
579 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200580
Damjan Marionf56b77a2016-10-03 19:44:57 +0200581 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200582 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200583 """
584 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200585
Klement Sekera75e7d132017-09-20 08:26:30 +0200586 :param interfaces: iterable interface indexes (if None,
587 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200588
Klement Sekeraf62ae122016-10-11 11:47:09 +0200589 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200590 if interfaces is None:
591 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200592 for i in interfaces:
593 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200594
Damjan Marionf56b77a2016-10-03 19:44:57 +0200595 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100596 def register_capture(cls, cap_name):
597 """ Register a capture in the testclass """
598 # add to the list of captures with current timestamp
599 cls._captures.append((time.time(), cap_name))
600 # filter out from zombies
601 cls._zombie_captures = [(stamp, name)
602 for (stamp, name) in cls._zombie_captures
603 if name != cap_name]
604
605 @classmethod
606 def pg_start(cls):
607 """ Remove any zombie captures and enable the packet generator """
608 # how long before capture is allowed to be deleted - otherwise vpp
609 # crashes - 100ms seems enough (this shouldn't be needed at all)
610 capture_ttl = 0.1
611 now = time.time()
612 for stamp, cap_name in cls._zombie_captures:
613 wait = stamp + capture_ttl - now
614 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100615 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100616 now = time.time()
617 cls.logger.debug("Removing zombie capture %s" % cap_name)
618 cls.vapi.cli('packet-generator delete %s' % cap_name)
619
Klement Sekeraf62ae122016-10-11 11:47:09 +0200620 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
621 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100622 cls._zombie_captures = cls._captures
623 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200624
Damjan Marionf56b77a2016-10-03 19:44:57 +0200625 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200626 def create_pg_interfaces(cls, interfaces):
627 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100628 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200629
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100630 :param interfaces: iterable indexes of the interfaces.
631 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200632
Klement Sekeraf62ae122016-10-11 11:47:09 +0200633 """
634 result = []
635 for i in interfaces:
636 intf = VppPGInterface(cls, i)
637 setattr(cls, intf.name, intf)
638 result.append(intf)
639 cls.pg_interfaces = result
640 return result
641
Matej Klotton0178d522016-11-04 11:11:44 +0100642 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200643 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100644 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100645 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100646
Klement Sekerab9ef2732018-06-24 22:49:33 +0200647 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100648 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100649 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200650 result = [VppLoInterface(cls) for i in range(count)]
651 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100652 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100653 cls.lo_interfaces = result
654 return result
655
Damjan Marionf56b77a2016-10-03 19:44:57 +0200656 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200657 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200658 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200659 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200660 NOTE: Currently works only when Raw layer is present.
661
662 :param packet: packet
663 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200664 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200665
666 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200667 packet_len = len(packet) + 4
668 extend = size - packet_len
669 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200670 num = (extend / len(padding)) + 1
671 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200672
Klement Sekeradab231a2016-12-21 08:50:14 +0100673 @classmethod
674 def reset_packet_infos(cls):
675 """ Reset the list of packet info objects and packet counts to zero """
676 cls._packet_infos = {}
677 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200678
Klement Sekeradab231a2016-12-21 08:50:14 +0100679 @classmethod
680 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200681 """
682 Create packet info object containing the source and destination indexes
683 and add it to the testcase's packet info list
684
Klement Sekeradab231a2016-12-21 08:50:14 +0100685 :param VppInterface src_if: source interface
686 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200687
688 :returns: _PacketInfo object
689
690 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200691 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100692 info.index = len(cls._packet_infos)
693 info.src = src_if.sw_if_index
694 info.dst = dst_if.sw_if_index
695 if isinstance(dst_if, VppSubInterface):
696 dst_idx = dst_if.parent.sw_if_index
697 else:
698 dst_idx = dst_if.sw_if_index
699 if dst_idx in cls._packet_count_for_dst_if_idx:
700 cls._packet_count_for_dst_if_idx[dst_idx] += 1
701 else:
702 cls._packet_count_for_dst_if_idx[dst_idx] = 1
703 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200704 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200705
Damjan Marionf56b77a2016-10-03 19:44:57 +0200706 @staticmethod
707 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200708 """
709 Convert _PacketInfo object to packet payload
710
711 :param info: _PacketInfo object
712
713 :returns: string containing serialized data from packet info
714 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100715 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
716 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200717
Damjan Marionf56b77a2016-10-03 19:44:57 +0200718 @staticmethod
719 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200720 """
721 Convert packet payload to _PacketInfo object
722
723 :param payload: packet payload
724
725 :returns: _PacketInfo object containing de-serialized data from payload
726
727 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200728 numbers = payload.split()
729 info = _PacketInfo()
730 info.index = int(numbers[0])
731 info.src = int(numbers[1])
732 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100733 info.ip = int(numbers[3])
734 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200735 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736
Damjan Marionf56b77a2016-10-03 19:44:57 +0200737 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200738 """
739 Iterate over the packet info list stored in the testcase
740 Start iteration with first element if info is None
741 Continue based on index in info if info is specified
742
743 :param info: info or None
744 :returns: next info in list or None if no more infos
745 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200746 if info is None:
747 next_index = 0
748 else:
749 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100750 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200751 return None
752 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100753 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200754
Klement Sekeraf62ae122016-10-11 11:47:09 +0200755 def get_next_packet_info_for_interface(self, src_index, info):
756 """
757 Search the packet info list for the next packet info with same source
758 interface index
759
760 :param src_index: source interface index to search for
761 :param info: packet info - where to start the search
762 :returns: packet info or None
763
764 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200765 while True:
766 info = self.get_next_packet_info(info)
767 if info is None:
768 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200769 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200770 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200771
Klement Sekeraf62ae122016-10-11 11:47:09 +0200772 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
773 """
774 Search the packet info list for the next packet info with same source
775 and destination interface indexes
776
777 :param src_index: source interface index to search for
778 :param dst_index: destination interface index to search for
779 :param info: packet info - where to start the search
780 :returns: packet info or None
781
782 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200783 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200784 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200785 if info is None:
786 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200787 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200788 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200789
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200790 def assert_equal(self, real_value, expected_value, name_or_class=None):
791 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100792 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200793 return
794 try:
795 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
796 msg = msg % (getdoc(name_or_class).strip(),
797 real_value, str(name_or_class(real_value)),
798 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100799 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200800 msg = "Invalid %s: %s does not match expected value %s" % (
801 name_or_class, real_value, expected_value)
802
803 self.assertEqual(real_value, expected_value, msg)
804
Klement Sekerab17dd962017-01-09 07:43:48 +0100805 def assert_in_range(self,
806 real_value,
807 expected_min,
808 expected_max,
809 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200810 if name is None:
811 msg = None
812 else:
813 msg = "Invalid %s: %s out of range <%s,%s>" % (
814 name, real_value, expected_min, expected_max)
815 self.assertTrue(expected_min <= real_value <= expected_max, msg)
816
Klement Sekerad81ae412018-05-16 10:52:54 +0200817 def assert_packet_checksums_valid(self, packet,
818 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200819 received = packet.__class__(str(packet))
820 self.logger.debug(
821 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200822 udp_layers = ['UDP', 'UDPerror']
823 checksum_fields = ['cksum', 'chksum']
824 checksums = []
825 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200826 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200827 while True:
828 layer = temp.getlayer(counter)
829 if layer:
830 for cf in checksum_fields:
831 if hasattr(layer, cf):
832 if ignore_zero_udp_checksums and \
833 0 == getattr(layer, cf) and \
834 layer.name in udp_layers:
835 continue
836 delattr(layer, cf)
837 checksums.append((counter, cf))
838 else:
839 break
840 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200841 if 0 == len(checksums):
842 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200843 temp = temp.__class__(str(temp))
844 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200845 calc_sum = getattr(temp[layer], cf)
846 self.assert_equal(
847 getattr(received[layer], cf), calc_sum,
848 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
849 self.logger.debug(
850 "Checksum field `%s` on `%s` layer has correct value `%s`" %
851 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200852
853 def assert_checksum_valid(self, received_packet, layer,
854 field_name='chksum',
855 ignore_zero_checksum=False):
856 """ Check checksum of received packet on given layer """
857 received_packet_checksum = getattr(received_packet[layer], field_name)
858 if ignore_zero_checksum and 0 == received_packet_checksum:
859 return
860 recalculated = received_packet.__class__(str(received_packet))
861 delattr(recalculated[layer], field_name)
862 recalculated = recalculated.__class__(str(recalculated))
863 self.assert_equal(received_packet_checksum,
864 getattr(recalculated[layer], field_name),
865 "packet checksum on layer: %s" % layer)
866
867 def assert_ip_checksum_valid(self, received_packet,
868 ignore_zero_checksum=False):
869 self.assert_checksum_valid(received_packet, 'IP',
870 ignore_zero_checksum=ignore_zero_checksum)
871
872 def assert_tcp_checksum_valid(self, received_packet,
873 ignore_zero_checksum=False):
874 self.assert_checksum_valid(received_packet, 'TCP',
875 ignore_zero_checksum=ignore_zero_checksum)
876
877 def assert_udp_checksum_valid(self, received_packet,
878 ignore_zero_checksum=True):
879 self.assert_checksum_valid(received_packet, 'UDP',
880 ignore_zero_checksum=ignore_zero_checksum)
881
882 def assert_embedded_icmp_checksum_valid(self, received_packet):
883 if received_packet.haslayer(IPerror):
884 self.assert_checksum_valid(received_packet, 'IPerror')
885 if received_packet.haslayer(TCPerror):
886 self.assert_checksum_valid(received_packet, 'TCPerror')
887 if received_packet.haslayer(UDPerror):
888 self.assert_checksum_valid(received_packet, 'UDPerror',
889 ignore_zero_checksum=True)
890 if received_packet.haslayer(ICMPerror):
891 self.assert_checksum_valid(received_packet, 'ICMPerror')
892
893 def assert_icmp_checksum_valid(self, received_packet):
894 self.assert_checksum_valid(received_packet, 'ICMP')
895 self.assert_embedded_icmp_checksum_valid(received_packet)
896
897 def assert_icmpv6_checksum_valid(self, pkt):
898 if pkt.haslayer(ICMPv6DestUnreach):
899 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
900 self.assert_embedded_icmp_checksum_valid(pkt)
901 if pkt.haslayer(ICMPv6EchoRequest):
902 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
903 if pkt.haslayer(ICMPv6EchoReply):
904 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
905
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100906 def assert_packet_counter_equal(self, counter, expected_value):
907 counters = self.vapi.cli("sh errors").split('\n')
908 counter_value = -1
909 for i in range(1, len(counters)-1):
910 results = counters[i].split()
911 if results[1] == counter:
912 counter_value = int(results[0])
913 break
914 self.assert_equal(counter_value, expected_value,
915 "packet counter `%s'" % counter)
916
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100917 @classmethod
918 def sleep(cls, timeout, remark=None):
919 if hasattr(cls, 'logger'):
Klement Sekera3cfa5582017-04-19 07:10:58 +0000920 cls.logger.debug("Starting sleep for %ss (%s)" % (timeout, remark))
921 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100922 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000923 after = time.time()
924 if after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200925 cls.logger.error("unexpected time.sleep() result - "
926 "slept for %ss instead of ~%ss!" % (
927 after - before, timeout))
Klement Sekera3cfa5582017-04-19 07:10:58 +0000928 if hasattr(cls, 'logger'):
929 cls.logger.debug(
930 "Finished sleep (%s) - slept %ss (wanted %ss)" % (
931 remark, after - before, timeout))
Klement Sekeraa57a9702017-02-02 06:58:07 +0100932
Neale Ranns947ea622018-06-07 23:48:20 -0700933 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800934 self.vapi.cli("clear trace")
935 intf.add_stream(pkts)
936 self.pg_enable_capture(self.pg_interfaces)
937 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700938 if not timeout:
939 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800940 for i in self.pg_interfaces:
941 i.get_capture(0, timeout=timeout)
942 i.assert_nothing_captured(remark=remark)
943 timeout = 0.1
944
945 def send_and_expect(self, input, pkts, output):
946 self.vapi.cli("clear trace")
947 input.add_stream(pkts)
948 self.pg_enable_capture(self.pg_interfaces)
949 self.pg_start()
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700950 if isinstance(object, (list,)):
951 rx = []
952 for o in output:
953 rx.append(output.get_capture(len(pkts)))
954 else:
955 rx = output.get_capture(len(pkts))
956 return rx
957
958 def send_and_expect_only(self, input, pkts, output, timeout=None):
959 self.vapi.cli("clear trace")
960 input.add_stream(pkts)
961 self.pg_enable_capture(self.pg_interfaces)
962 self.pg_start()
963 if isinstance(object, (list,)):
964 outputs = output
965 rx = []
966 for o in outputs:
967 rx.append(output.get_capture(len(pkts)))
968 else:
969 rx = output.get_capture(len(pkts))
970 outputs = [output]
971 if not timeout:
972 timeout = 1
973 for i in self.pg_interfaces:
974 if i not in outputs:
975 i.get_capture(0, timeout=timeout)
976 i.assert_nothing_captured()
977 timeout = 0.1
978
Neale Ranns52fae862018-01-08 04:41:42 -0800979 return rx
980
Damjan Marionf56b77a2016-10-03 19:44:57 +0200981
juraj.linkes184870a2018-07-16 14:22:01 +0200982def get_testcase_doc_name(test):
983 return getdoc(test.__class__).splitlines()[0]
984
985
Ole Trøan5ba91592018-11-22 10:01:09 +0000986def get_test_description(descriptions, test):
987 short_description = test.shortDescription()
988 if descriptions and short_description:
989 return short_description
990 else:
991 return str(test)
992
993
juraj.linkes40dd73b2018-09-21 13:55:16 +0200994class TestCaseInfo(object):
995 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
996 self.logger = logger
997 self.tempdir = tempdir
998 self.vpp_pid = vpp_pid
999 self.vpp_bin_path = vpp_bin_path
1000 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001001
1002
Damjan Marionf56b77a2016-10-03 19:44:57 +02001003class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001004 """
1005 @property result_string
1006 String variable to store the test case result string.
1007 @property errors
1008 List variable containing 2-tuples of TestCase instances and strings
1009 holding formatted tracebacks. Each tuple represents a test which
1010 raised an unexpected exception.
1011 @property failures
1012 List variable containing 2-tuples of TestCase instances and strings
1013 holding formatted tracebacks. Each tuple represents a test where
1014 a failure was explicitly signalled using the TestCase.assert*()
1015 methods.
1016 """
1017
juraj.linkes40dd73b2018-09-21 13:55:16 +02001018 failed_test_cases_info = set()
1019 core_crash_test_cases_info = set()
1020 current_test_case_info = None
1021
Damjan Marionf56b77a2016-10-03 19:44:57 +02001022 def __init__(self, stream, descriptions, verbosity):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001023 """
Klement Sekerada505f62017-01-04 12:58:53 +01001024 :param stream File descriptor to store where to report test results.
1025 Set to the standard error stream by default.
1026 :param descriptions Boolean variable to store information if to use
1027 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001028 :param verbosity Integer variable to store required verbosity level.
1029 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001030 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
1031 self.stream = stream
1032 self.descriptions = descriptions
1033 self.verbosity = verbosity
1034 self.result_string = None
Damjan Marionf56b77a2016-10-03 19:44:57 +02001035
Damjan Marionf56b77a2016-10-03 19:44:57 +02001036 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001037 """
1038 Record a test succeeded result
1039
1040 :param test:
1041
1042 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001043 if self.current_test_case_info:
1044 self.current_test_case_info.logger.debug(
1045 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1046 test._testMethodName,
1047 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001048 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001049 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001050
juraj.linkescae64f82018-09-19 15:01:47 +02001051 self.send_result_through_pipe(test, PASS)
1052
Klement Sekeraf62ae122016-10-11 11:47:09 +02001053 def addSkip(self, test, reason):
1054 """
1055 Record a test skipped.
1056
1057 :param test:
1058 :param reason:
1059
1060 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001061 if self.current_test_case_info:
1062 self.current_test_case_info.logger.debug(
1063 "--- addSkip() %s.%s(%s) called, reason is %s" %
1064 (test.__class__.__name__, test._testMethodName,
1065 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001066 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001067 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001068
juraj.linkescae64f82018-09-19 15:01:47 +02001069 self.send_result_through_pipe(test, SKIP)
1070
juraj.linkes40dd73b2018-09-21 13:55:16 +02001071 def symlink_failed(self):
1072 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001073 try:
1074 failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001075 link_path = os.path.join(
1076 failed_dir,
1077 '%s-FAILED' %
1078 os.path.basename(self.current_test_case_info.tempdir))
1079 if self.current_test_case_info.logger:
1080 self.current_test_case_info.logger.debug(
1081 "creating a link to the failed test")
1082 self.current_test_case_info.logger.debug(
1083 "os.symlink(%s, %s)" %
1084 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001085 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001086 if self.current_test_case_info.logger:
1087 self.current_test_case_info.logger.debug(
1088 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001089 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001090 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001091
Klement Sekeraf413bef2017-08-15 07:09:02 +02001092 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001093 if self.current_test_case_info.logger:
1094 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001095
juraj.linkescae64f82018-09-19 15:01:47 +02001096 def send_result_through_pipe(self, test, result):
1097 if hasattr(self, 'test_framework_result_pipe'):
1098 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001099 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001100 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001101
juraj.linkes40dd73b2018-09-21 13:55:16 +02001102 def log_error(self, test, err, fn_name):
1103 if self.current_test_case_info:
1104 if isinstance(test, unittest.suite._ErrorHolder):
1105 test_name = test.description
1106 else:
1107 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1108 test._testMethodName,
1109 test._testMethodDoc)
1110 self.current_test_case_info.logger.debug(
1111 "--- %s() %s called, err is %s" %
1112 (fn_name, test_name, err))
1113 self.current_test_case_info.logger.debug(
1114 "formatted exception is:\n%s" %
1115 "".join(format_exception(*err)))
1116
1117 def add_error(self, test, err, unittest_fn, error_type):
1118 if error_type == FAIL:
1119 self.log_error(test, err, 'addFailure')
1120 error_type_str = colorize("FAIL", RED)
1121 elif error_type == ERROR:
1122 self.log_error(test, err, 'addError')
1123 error_type_str = colorize("ERROR", RED)
1124 else:
1125 raise Exception('Error type %s cannot be used to record an '
1126 'error or a failure' % error_type)
1127
1128 unittest_fn(self, test, err)
1129 if self.current_test_case_info:
1130 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1131 (error_type_str,
1132 self.current_test_case_info.tempdir)
1133 self.symlink_failed()
1134 self.failed_test_cases_info.add(self.current_test_case_info)
1135 if is_core_present(self.current_test_case_info.tempdir):
1136 if not self.current_test_case_info.core_crash_test:
1137 if isinstance(test, unittest.suite._ErrorHolder):
1138 test_name = str(test)
1139 else:
1140 test_name = "'{}' ({})".format(
1141 get_testcase_doc_name(test), test.id())
1142 self.current_test_case_info.core_crash_test = test_name
1143 self.core_crash_test_cases_info.add(
1144 self.current_test_case_info)
1145 else:
1146 self.result_string = '%s [no temp dir]' % error_type_str
1147
1148 self.send_result_through_pipe(test, error_type)
1149
Damjan Marionf56b77a2016-10-03 19:44:57 +02001150 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001151 """
1152 Record a test failed result
1153
1154 :param test:
1155 :param err: error message
1156
1157 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001158 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001159
Damjan Marionf56b77a2016-10-03 19:44:57 +02001160 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001161 """
1162 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001163
Klement Sekeraf62ae122016-10-11 11:47:09 +02001164 :param test:
1165 :param err: error message
1166
1167 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001168 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001169
Damjan Marionf56b77a2016-10-03 19:44:57 +02001170 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001171 """
1172 Get test description
1173
1174 :param test:
1175 :returns: test description
1176
1177 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001178 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001179
Damjan Marionf56b77a2016-10-03 19:44:57 +02001180 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001181 """
1182 Start a test
1183
1184 :param test:
1185
1186 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001187 test.print_header(test.__class__)
1188
Damjan Marionf56b77a2016-10-03 19:44:57 +02001189 unittest.TestResult.startTest(self, test)
1190 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001191 self.stream.writeln(
1192 "Starting " + self.getDescription(test) + " ...")
1193 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001194
Damjan Marionf56b77a2016-10-03 19:44:57 +02001195 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001196 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001197 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001198
1199 :param test:
1200
1201 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001202 unittest.TestResult.stopTest(self, test)
1203 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001204 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001205 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001206 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001207 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001208 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001209 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001210 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001211
1212 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001213
Damjan Marionf56b77a2016-10-03 19:44:57 +02001214 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001215 """
1216 Print errors from running the test case
1217 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001218 self.stream.writeln()
1219 self.printErrorList('ERROR', self.errors)
1220 self.printErrorList('FAIL', self.failures)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001221
Damjan Marionf56b77a2016-10-03 19:44:57 +02001222 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001223 """
1224 Print error list to the output stream together with error type
1225 and test case description.
1226
1227 :param flavour: error type
1228 :param errors: iterable errors
1229
1230 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001231 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001232 self.stream.writeln(double_line_delim)
1233 self.stream.writeln("%s: %s" %
1234 (flavour, self.getDescription(test)))
1235 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001236 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001237
1238
Damjan Marionf56b77a2016-10-03 19:44:57 +02001239class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001240 """
Klement Sekera104543f2017-02-03 07:29:43 +01001241 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001242 """
1243 @property
1244 def resultclass(self):
1245 """Class maintaining the results of the tests"""
1246 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001247
juraj.linkes184870a2018-07-16 14:22:01 +02001248 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001249 result_pipe=None, failfast=False, buffer=False,
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001250 resultclass=None):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001251
Klement Sekera7a161da2017-01-17 13:42:48 +01001252 # ignore stream setting here, use hard-coded stdout to be in sync
1253 # with prints from VppTestCase methods ...
1254 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1255 verbosity, failfast, buffer,
1256 resultclass)
Klement Sekera909a6a12017-08-08 04:33:53 +02001257 reporter = KeepAliveReporter()
Klement Sekeradf2b9802017-10-05 10:26:03 +02001258 reporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001259
juraj.linkescae64f82018-09-19 15:01:47 +02001260 VppTestResult.test_framework_result_pipe = result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001261
Damjan Marionf56b77a2016-10-03 19:44:57 +02001262 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001263 """
1264 Run the tests
1265
1266 :param test:
1267
1268 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001269 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001270
1271 result = super(VppTestRunner, self).run(test)
1272 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001273
1274
1275class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001276 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001277 self.logger = logger
1278 self.args = args
1279 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001280 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001281 super(Worker, self).__init__()
1282
1283 def run(self):
1284 executable = self.args[0]
1285 self.logger.debug("Running executable w/args `%s'" % self.args)
1286 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001287 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001288 env["CK_LOG_FILE_NAME"] = "-"
1289 self.process = subprocess.Popen(
1290 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1291 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1292 out, err = self.process.communicate()
1293 self.logger.debug("Finished running `%s'" % executable)
1294 self.logger.info("Return code is `%s'" % self.process.returncode)
1295 self.logger.info(single_line_delim)
1296 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1297 self.logger.info(single_line_delim)
1298 self.logger.info(out)
1299 self.logger.info(single_line_delim)
1300 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1301 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001302 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001303 self.logger.info(single_line_delim)
1304 self.result = self.process.returncode