blob: 4f830ba613e3386db662be3996b9bd5d705b8589 [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)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100382 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200383 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200384 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200385 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200386 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
387 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100388 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
389 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200390 cls.file_handler.setLevel(DEBUG)
391 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200392 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200393 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200394 cls.logger.info("Temporary dir is %s, shm prefix is %s",
395 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200396 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100397 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100398 cls._captures = []
399 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200400 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100401 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100402 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200403 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200404 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200405 # need to catch exceptions here because if we raise, then the cleanup
406 # doesn't get called and we might end with a zombie vpp
407 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200408 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200409 cls.reporter.send_keep_alive(cls, 'setUpClass')
410 VppTestResult.current_test_case_info = TestCaseInfo(
411 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100412 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100413 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100414 cls.pump_thread_stop_flag = Event()
415 cls.pump_thread_wakeup_pipe = os.pipe()
416 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100417 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100418 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200419 if cls.debug_gdb or cls.debug_gdbserver:
420 read_timeout = 0
421 else:
422 read_timeout = 5
423 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
424 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100425 if cls.step:
426 hook = StepHook(cls)
427 else:
428 hook = PollHook(cls)
429 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200430 cls.wait_for_stats_socket()
431 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200432 try:
433 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100434 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200435 cls.vpp_startup_failed = True
436 cls.logger.critical(
437 "VPP died shortly after startup, check the"
438 " output to standard error for possible cause")
439 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100440 try:
441 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100442 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100443 try:
444 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100445 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100446 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100447 if cls.debug_gdbserver:
448 print(colorize("You're running VPP inside gdbserver but "
449 "VPP-API connection failed, did you forget "
450 "to 'continue' VPP from within gdb?", RED))
451 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100452 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100453 try:
454 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100455 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100456 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100457 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200458
Damjan Marionf56b77a2016-10-03 19:44:57 +0200459 @classmethod
460 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200461 """
462 Disconnect vpp-api, kill vpp and cleanup shared memory files
463 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200464 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
465 cls.vpp.poll()
466 if cls.vpp.returncode is None:
467 print(double_line_delim)
468 print("VPP or GDB server is still running")
469 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100470 raw_input("When done debugging, press ENTER to kill the "
471 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200472
juraj.linkes184870a2018-07-16 14:22:01 +0200473 # first signal that we want to stop the pump thread, then wake it up
474 if hasattr(cls, 'pump_thread_stop_flag'):
475 cls.pump_thread_stop_flag.set()
476 if hasattr(cls, 'pump_thread_wakeup_pipe'):
477 os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100478 if hasattr(cls, 'pump_thread'):
479 cls.logger.debug("Waiting for pump thread to stop")
480 cls.pump_thread.join()
481 if hasattr(cls, 'vpp_stderr_reader_thread'):
482 cls.logger.debug("Waiting for stdderr pump to stop")
483 cls.vpp_stderr_reader_thread.join()
484
Klement Sekeraf62ae122016-10-11 11:47:09 +0200485 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100486 if hasattr(cls, 'vapi'):
487 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100488 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200489 cls.vpp.poll()
490 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100491 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200492 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100493 cls.logger.debug("Waiting for vpp to die")
494 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200495 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200496
Klement Sekera3747c752017-04-10 06:30:17 +0200497 if cls.vpp_startup_failed:
498 stdout_log = cls.logger.info
499 stderr_log = cls.logger.critical
500 else:
501 stdout_log = cls.logger.info
502 stderr_log = cls.logger.info
503
Klement Sekerae4504c62016-12-08 10:16:41 +0100504 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200505 stdout_log(single_line_delim)
506 stdout_log('VPP output to stdout while running %s:', cls.__name__)
507 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100508 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200509 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
510 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200511 stdout_log('\n%s', vpp_output)
512 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200513
Klement Sekerae4504c62016-12-08 10:16:41 +0100514 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200515 stderr_log(single_line_delim)
516 stderr_log('VPP output to stderr while running %s:', cls.__name__)
517 stderr_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100518 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200519 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
520 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200521 stderr_log('\n%s', vpp_output)
522 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200523
Damjan Marionf56b77a2016-10-03 19:44:57 +0200524 @classmethod
525 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200526 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200527 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200528 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200529 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100530 cls.reset_packet_infos()
531 if debug_framework:
532 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200533
Damjan Marionf56b77a2016-10-03 19:44:57 +0200534 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200535 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100536 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
537 (self.__class__.__name__, self._testMethodName,
538 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200539 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200540 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700541 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200542 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200543 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200544 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800545 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100546 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500547 # Save/Dump VPP api trace log
548 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
549 tmp_api_trace = "/tmp/%s" % api_trace
550 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
551 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
552 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
553 vpp_api_trace_log))
554 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500555 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500556 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100557 else:
558 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200559
Damjan Marionf56b77a2016-10-03 19:44:57 +0200560 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200561 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200562 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100563 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
564 (self.__class__.__name__, self._testMethodName,
565 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100566 if self.vpp_dead:
567 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100568 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100569 self.vpp_stdout_deque.append(
570 "--- test setUp() for %s.%s(%s) starts here ---\n" %
571 (self.__class__.__name__, self._testMethodName,
572 self._testMethodDoc))
573 self.vpp_stderr_deque.append(
574 "--- test setUp() for %s.%s(%s) starts here ---\n" %
575 (self.__class__.__name__, self._testMethodName,
576 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200577 self.vapi.cli("clear trace")
578 # store the test instance inside the test class - so that objects
579 # holding the class can access instance methods (like assertEqual)
580 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200581
Damjan Marionf56b77a2016-10-03 19:44:57 +0200582 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200583 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200584 """
585 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200586
Klement Sekera75e7d132017-09-20 08:26:30 +0200587 :param interfaces: iterable interface indexes (if None,
588 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200589
Klement Sekeraf62ae122016-10-11 11:47:09 +0200590 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200591 if interfaces is None:
592 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200593 for i in interfaces:
594 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200595
Damjan Marionf56b77a2016-10-03 19:44:57 +0200596 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100597 def register_capture(cls, cap_name):
598 """ Register a capture in the testclass """
599 # add to the list of captures with current timestamp
600 cls._captures.append((time.time(), cap_name))
601 # filter out from zombies
602 cls._zombie_captures = [(stamp, name)
603 for (stamp, name) in cls._zombie_captures
604 if name != cap_name]
605
606 @classmethod
607 def pg_start(cls):
608 """ Remove any zombie captures and enable the packet generator """
609 # how long before capture is allowed to be deleted - otherwise vpp
610 # crashes - 100ms seems enough (this shouldn't be needed at all)
611 capture_ttl = 0.1
612 now = time.time()
613 for stamp, cap_name in cls._zombie_captures:
614 wait = stamp + capture_ttl - now
615 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100616 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100617 now = time.time()
618 cls.logger.debug("Removing zombie capture %s" % cap_name)
619 cls.vapi.cli('packet-generator delete %s' % cap_name)
620
Klement Sekeraf62ae122016-10-11 11:47:09 +0200621 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
622 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100623 cls._zombie_captures = cls._captures
624 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200625
Damjan Marionf56b77a2016-10-03 19:44:57 +0200626 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200627 def create_pg_interfaces(cls, interfaces):
628 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100629 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200630
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100631 :param interfaces: iterable indexes of the interfaces.
632 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200633
Klement Sekeraf62ae122016-10-11 11:47:09 +0200634 """
635 result = []
636 for i in interfaces:
637 intf = VppPGInterface(cls, i)
638 setattr(cls, intf.name, intf)
639 result.append(intf)
640 cls.pg_interfaces = result
641 return result
642
Matej Klotton0178d522016-11-04 11:11:44 +0100643 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200644 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100645 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100646 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100647
Klement Sekerab9ef2732018-06-24 22:49:33 +0200648 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100649 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100650 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200651 result = [VppLoInterface(cls) for i in range(count)]
652 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100653 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100654 cls.lo_interfaces = result
655 return result
656
Damjan Marionf56b77a2016-10-03 19:44:57 +0200657 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200658 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200659 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200660 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200661 NOTE: Currently works only when Raw layer is present.
662
663 :param packet: packet
664 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200665 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200666
667 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200668 packet_len = len(packet) + 4
669 extend = size - packet_len
670 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200671 num = (extend / len(padding)) + 1
672 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200673
Klement Sekeradab231a2016-12-21 08:50:14 +0100674 @classmethod
675 def reset_packet_infos(cls):
676 """ Reset the list of packet info objects and packet counts to zero """
677 cls._packet_infos = {}
678 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200679
Klement Sekeradab231a2016-12-21 08:50:14 +0100680 @classmethod
681 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200682 """
683 Create packet info object containing the source and destination indexes
684 and add it to the testcase's packet info list
685
Klement Sekeradab231a2016-12-21 08:50:14 +0100686 :param VppInterface src_if: source interface
687 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200688
689 :returns: _PacketInfo object
690
691 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200692 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100693 info.index = len(cls._packet_infos)
694 info.src = src_if.sw_if_index
695 info.dst = dst_if.sw_if_index
696 if isinstance(dst_if, VppSubInterface):
697 dst_idx = dst_if.parent.sw_if_index
698 else:
699 dst_idx = dst_if.sw_if_index
700 if dst_idx in cls._packet_count_for_dst_if_idx:
701 cls._packet_count_for_dst_if_idx[dst_idx] += 1
702 else:
703 cls._packet_count_for_dst_if_idx[dst_idx] = 1
704 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200705 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200706
Damjan Marionf56b77a2016-10-03 19:44:57 +0200707 @staticmethod
708 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200709 """
710 Convert _PacketInfo object to packet payload
711
712 :param info: _PacketInfo object
713
714 :returns: string containing serialized data from packet info
715 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100716 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
717 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200718
Damjan Marionf56b77a2016-10-03 19:44:57 +0200719 @staticmethod
720 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200721 """
722 Convert packet payload to _PacketInfo object
723
724 :param payload: packet payload
725
726 :returns: _PacketInfo object containing de-serialized data from payload
727
728 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200729 numbers = payload.split()
730 info = _PacketInfo()
731 info.index = int(numbers[0])
732 info.src = int(numbers[1])
733 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100734 info.ip = int(numbers[3])
735 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200737
Damjan Marionf56b77a2016-10-03 19:44:57 +0200738 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200739 """
740 Iterate over the packet info list stored in the testcase
741 Start iteration with first element if info is None
742 Continue based on index in info if info is specified
743
744 :param info: info or None
745 :returns: next info in list or None if no more infos
746 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200747 if info is None:
748 next_index = 0
749 else:
750 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100751 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200752 return None
753 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100754 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200755
Klement Sekeraf62ae122016-10-11 11:47:09 +0200756 def get_next_packet_info_for_interface(self, src_index, info):
757 """
758 Search the packet info list for the next packet info with same source
759 interface index
760
761 :param src_index: source interface index to search for
762 :param info: packet info - where to start the search
763 :returns: packet info or None
764
765 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200766 while True:
767 info = self.get_next_packet_info(info)
768 if info is None:
769 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200770 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200771 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200772
Klement Sekeraf62ae122016-10-11 11:47:09 +0200773 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
774 """
775 Search the packet info list for the next packet info with same source
776 and destination interface indexes
777
778 :param src_index: source interface index to search for
779 :param dst_index: destination interface index to search for
780 :param info: packet info - where to start the search
781 :returns: packet info or None
782
783 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200785 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200786 if info is None:
787 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200788 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200789 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200790
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200791 def assert_equal(self, real_value, expected_value, name_or_class=None):
792 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100793 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200794 return
795 try:
796 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
797 msg = msg % (getdoc(name_or_class).strip(),
798 real_value, str(name_or_class(real_value)),
799 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100800 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200801 msg = "Invalid %s: %s does not match expected value %s" % (
802 name_or_class, real_value, expected_value)
803
804 self.assertEqual(real_value, expected_value, msg)
805
Klement Sekerab17dd962017-01-09 07:43:48 +0100806 def assert_in_range(self,
807 real_value,
808 expected_min,
809 expected_max,
810 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200811 if name is None:
812 msg = None
813 else:
814 msg = "Invalid %s: %s out of range <%s,%s>" % (
815 name, real_value, expected_min, expected_max)
816 self.assertTrue(expected_min <= real_value <= expected_max, msg)
817
Klement Sekerad81ae412018-05-16 10:52:54 +0200818 def assert_packet_checksums_valid(self, packet,
819 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200820 received = packet.__class__(str(packet))
821 self.logger.debug(
822 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200823 udp_layers = ['UDP', 'UDPerror']
824 checksum_fields = ['cksum', 'chksum']
825 checksums = []
826 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200827 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200828 while True:
829 layer = temp.getlayer(counter)
830 if layer:
831 for cf in checksum_fields:
832 if hasattr(layer, cf):
833 if ignore_zero_udp_checksums and \
834 0 == getattr(layer, cf) and \
835 layer.name in udp_layers:
836 continue
837 delattr(layer, cf)
838 checksums.append((counter, cf))
839 else:
840 break
841 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200842 if 0 == len(checksums):
843 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200844 temp = temp.__class__(str(temp))
845 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200846 calc_sum = getattr(temp[layer], cf)
847 self.assert_equal(
848 getattr(received[layer], cf), calc_sum,
849 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
850 self.logger.debug(
851 "Checksum field `%s` on `%s` layer has correct value `%s`" %
852 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200853
854 def assert_checksum_valid(self, received_packet, layer,
855 field_name='chksum',
856 ignore_zero_checksum=False):
857 """ Check checksum of received packet on given layer """
858 received_packet_checksum = getattr(received_packet[layer], field_name)
859 if ignore_zero_checksum and 0 == received_packet_checksum:
860 return
861 recalculated = received_packet.__class__(str(received_packet))
862 delattr(recalculated[layer], field_name)
863 recalculated = recalculated.__class__(str(recalculated))
864 self.assert_equal(received_packet_checksum,
865 getattr(recalculated[layer], field_name),
866 "packet checksum on layer: %s" % layer)
867
868 def assert_ip_checksum_valid(self, received_packet,
869 ignore_zero_checksum=False):
870 self.assert_checksum_valid(received_packet, 'IP',
871 ignore_zero_checksum=ignore_zero_checksum)
872
873 def assert_tcp_checksum_valid(self, received_packet,
874 ignore_zero_checksum=False):
875 self.assert_checksum_valid(received_packet, 'TCP',
876 ignore_zero_checksum=ignore_zero_checksum)
877
878 def assert_udp_checksum_valid(self, received_packet,
879 ignore_zero_checksum=True):
880 self.assert_checksum_valid(received_packet, 'UDP',
881 ignore_zero_checksum=ignore_zero_checksum)
882
883 def assert_embedded_icmp_checksum_valid(self, received_packet):
884 if received_packet.haslayer(IPerror):
885 self.assert_checksum_valid(received_packet, 'IPerror')
886 if received_packet.haslayer(TCPerror):
887 self.assert_checksum_valid(received_packet, 'TCPerror')
888 if received_packet.haslayer(UDPerror):
889 self.assert_checksum_valid(received_packet, 'UDPerror',
890 ignore_zero_checksum=True)
891 if received_packet.haslayer(ICMPerror):
892 self.assert_checksum_valid(received_packet, 'ICMPerror')
893
894 def assert_icmp_checksum_valid(self, received_packet):
895 self.assert_checksum_valid(received_packet, 'ICMP')
896 self.assert_embedded_icmp_checksum_valid(received_packet)
897
898 def assert_icmpv6_checksum_valid(self, pkt):
899 if pkt.haslayer(ICMPv6DestUnreach):
900 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
901 self.assert_embedded_icmp_checksum_valid(pkt)
902 if pkt.haslayer(ICMPv6EchoRequest):
903 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
904 if pkt.haslayer(ICMPv6EchoReply):
905 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
906
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100907 def assert_packet_counter_equal(self, counter, expected_value):
908 counters = self.vapi.cli("sh errors").split('\n')
909 counter_value = -1
910 for i in range(1, len(counters)-1):
911 results = counters[i].split()
912 if results[1] == counter:
913 counter_value = int(results[0])
914 break
915 self.assert_equal(counter_value, expected_value,
916 "packet counter `%s'" % counter)
917
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100918 @classmethod
919 def sleep(cls, timeout, remark=None):
920 if hasattr(cls, 'logger'):
Klement Sekera3cfa5582017-04-19 07:10:58 +0000921 cls.logger.debug("Starting sleep for %ss (%s)" % (timeout, remark))
922 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100923 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000924 after = time.time()
925 if after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200926 cls.logger.error("unexpected time.sleep() result - "
927 "slept for %ss instead of ~%ss!" % (
928 after - before, timeout))
Klement Sekera3cfa5582017-04-19 07:10:58 +0000929 if hasattr(cls, 'logger'):
930 cls.logger.debug(
931 "Finished sleep (%s) - slept %ss (wanted %ss)" % (
932 remark, after - before, timeout))
Klement Sekeraa57a9702017-02-02 06:58:07 +0100933
Neale Ranns947ea622018-06-07 23:48:20 -0700934 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800935 self.vapi.cli("clear trace")
936 intf.add_stream(pkts)
937 self.pg_enable_capture(self.pg_interfaces)
938 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700939 if not timeout:
940 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800941 for i in self.pg_interfaces:
942 i.get_capture(0, timeout=timeout)
943 i.assert_nothing_captured(remark=remark)
944 timeout = 0.1
945
946 def send_and_expect(self, input, pkts, output):
947 self.vapi.cli("clear trace")
948 input.add_stream(pkts)
949 self.pg_enable_capture(self.pg_interfaces)
950 self.pg_start()
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700951 if isinstance(object, (list,)):
952 rx = []
953 for o in output:
954 rx.append(output.get_capture(len(pkts)))
955 else:
956 rx = output.get_capture(len(pkts))
957 return rx
958
959 def send_and_expect_only(self, input, pkts, output, timeout=None):
960 self.vapi.cli("clear trace")
961 input.add_stream(pkts)
962 self.pg_enable_capture(self.pg_interfaces)
963 self.pg_start()
964 if isinstance(object, (list,)):
965 outputs = output
966 rx = []
967 for o in outputs:
968 rx.append(output.get_capture(len(pkts)))
969 else:
970 rx = output.get_capture(len(pkts))
971 outputs = [output]
972 if not timeout:
973 timeout = 1
974 for i in self.pg_interfaces:
975 if i not in outputs:
976 i.get_capture(0, timeout=timeout)
977 i.assert_nothing_captured()
978 timeout = 0.1
979
Neale Ranns52fae862018-01-08 04:41:42 -0800980 return rx
981
Damjan Marionf56b77a2016-10-03 19:44:57 +0200982
juraj.linkes184870a2018-07-16 14:22:01 +0200983def get_testcase_doc_name(test):
984 return getdoc(test.__class__).splitlines()[0]
985
986
Ole Trøan5ba91592018-11-22 10:01:09 +0000987def get_test_description(descriptions, test):
988 short_description = test.shortDescription()
989 if descriptions and short_description:
990 return short_description
991 else:
992 return str(test)
993
994
juraj.linkes40dd73b2018-09-21 13:55:16 +0200995class TestCaseInfo(object):
996 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
997 self.logger = logger
998 self.tempdir = tempdir
999 self.vpp_pid = vpp_pid
1000 self.vpp_bin_path = vpp_bin_path
1001 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001002
1003
Damjan Marionf56b77a2016-10-03 19:44:57 +02001004class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001005 """
1006 @property result_string
1007 String variable to store the test case result string.
1008 @property errors
1009 List variable containing 2-tuples of TestCase instances and strings
1010 holding formatted tracebacks. Each tuple represents a test which
1011 raised an unexpected exception.
1012 @property failures
1013 List variable containing 2-tuples of TestCase instances and strings
1014 holding formatted tracebacks. Each tuple represents a test where
1015 a failure was explicitly signalled using the TestCase.assert*()
1016 methods.
1017 """
1018
juraj.linkes40dd73b2018-09-21 13:55:16 +02001019 failed_test_cases_info = set()
1020 core_crash_test_cases_info = set()
1021 current_test_case_info = None
1022
Damjan Marionf56b77a2016-10-03 19:44:57 +02001023 def __init__(self, stream, descriptions, verbosity):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001024 """
Klement Sekerada505f62017-01-04 12:58:53 +01001025 :param stream File descriptor to store where to report test results.
1026 Set to the standard error stream by default.
1027 :param descriptions Boolean variable to store information if to use
1028 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001029 :param verbosity Integer variable to store required verbosity level.
1030 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001031 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
1032 self.stream = stream
1033 self.descriptions = descriptions
1034 self.verbosity = verbosity
1035 self.result_string = None
Damjan Marionf56b77a2016-10-03 19:44:57 +02001036
Damjan Marionf56b77a2016-10-03 19:44:57 +02001037 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001038 """
1039 Record a test succeeded result
1040
1041 :param test:
1042
1043 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001044 if self.current_test_case_info:
1045 self.current_test_case_info.logger.debug(
1046 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1047 test._testMethodName,
1048 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001049 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001050 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001051
juraj.linkescae64f82018-09-19 15:01:47 +02001052 self.send_result_through_pipe(test, PASS)
1053
Klement Sekeraf62ae122016-10-11 11:47:09 +02001054 def addSkip(self, test, reason):
1055 """
1056 Record a test skipped.
1057
1058 :param test:
1059 :param reason:
1060
1061 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001062 if self.current_test_case_info:
1063 self.current_test_case_info.logger.debug(
1064 "--- addSkip() %s.%s(%s) called, reason is %s" %
1065 (test.__class__.__name__, test._testMethodName,
1066 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001067 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001068 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001069
juraj.linkescae64f82018-09-19 15:01:47 +02001070 self.send_result_through_pipe(test, SKIP)
1071
juraj.linkes40dd73b2018-09-21 13:55:16 +02001072 def symlink_failed(self):
1073 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001074 try:
1075 failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001076 link_path = os.path.join(
1077 failed_dir,
1078 '%s-FAILED' %
1079 os.path.basename(self.current_test_case_info.tempdir))
1080 if self.current_test_case_info.logger:
1081 self.current_test_case_info.logger.debug(
1082 "creating a link to the failed test")
1083 self.current_test_case_info.logger.debug(
1084 "os.symlink(%s, %s)" %
1085 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001086 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001087 if self.current_test_case_info.logger:
1088 self.current_test_case_info.logger.debug(
1089 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001090 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001091 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001092
Klement Sekeraf413bef2017-08-15 07:09:02 +02001093 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001094 if self.current_test_case_info.logger:
1095 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001096
juraj.linkescae64f82018-09-19 15:01:47 +02001097 def send_result_through_pipe(self, test, result):
1098 if hasattr(self, 'test_framework_result_pipe'):
1099 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001100 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001101 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001102
juraj.linkes40dd73b2018-09-21 13:55:16 +02001103 def log_error(self, test, err, fn_name):
1104 if self.current_test_case_info:
1105 if isinstance(test, unittest.suite._ErrorHolder):
1106 test_name = test.description
1107 else:
1108 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1109 test._testMethodName,
1110 test._testMethodDoc)
1111 self.current_test_case_info.logger.debug(
1112 "--- %s() %s called, err is %s" %
1113 (fn_name, test_name, err))
1114 self.current_test_case_info.logger.debug(
1115 "formatted exception is:\n%s" %
1116 "".join(format_exception(*err)))
1117
1118 def add_error(self, test, err, unittest_fn, error_type):
1119 if error_type == FAIL:
1120 self.log_error(test, err, 'addFailure')
1121 error_type_str = colorize("FAIL", RED)
1122 elif error_type == ERROR:
1123 self.log_error(test, err, 'addError')
1124 error_type_str = colorize("ERROR", RED)
1125 else:
1126 raise Exception('Error type %s cannot be used to record an '
1127 'error or a failure' % error_type)
1128
1129 unittest_fn(self, test, err)
1130 if self.current_test_case_info:
1131 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1132 (error_type_str,
1133 self.current_test_case_info.tempdir)
1134 self.symlink_failed()
1135 self.failed_test_cases_info.add(self.current_test_case_info)
1136 if is_core_present(self.current_test_case_info.tempdir):
1137 if not self.current_test_case_info.core_crash_test:
1138 if isinstance(test, unittest.suite._ErrorHolder):
1139 test_name = str(test)
1140 else:
1141 test_name = "'{}' ({})".format(
1142 get_testcase_doc_name(test), test.id())
1143 self.current_test_case_info.core_crash_test = test_name
1144 self.core_crash_test_cases_info.add(
1145 self.current_test_case_info)
1146 else:
1147 self.result_string = '%s [no temp dir]' % error_type_str
1148
1149 self.send_result_through_pipe(test, error_type)
1150
Damjan Marionf56b77a2016-10-03 19:44:57 +02001151 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001152 """
1153 Record a test failed result
1154
1155 :param test:
1156 :param err: error message
1157
1158 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001159 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001160
Damjan Marionf56b77a2016-10-03 19:44:57 +02001161 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001162 """
1163 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001164
Klement Sekeraf62ae122016-10-11 11:47:09 +02001165 :param test:
1166 :param err: error message
1167
1168 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001169 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001170
Damjan Marionf56b77a2016-10-03 19:44:57 +02001171 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001172 """
1173 Get test description
1174
1175 :param test:
1176 :returns: test description
1177
1178 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001179 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001180
Damjan Marionf56b77a2016-10-03 19:44:57 +02001181 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001182 """
1183 Start a test
1184
1185 :param test:
1186
1187 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001188 test.print_header(test.__class__)
1189
Damjan Marionf56b77a2016-10-03 19:44:57 +02001190 unittest.TestResult.startTest(self, test)
1191 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001192 self.stream.writeln(
1193 "Starting " + self.getDescription(test) + " ...")
1194 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001195
Damjan Marionf56b77a2016-10-03 19:44:57 +02001196 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001197 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001198 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001199
1200 :param test:
1201
1202 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001203 unittest.TestResult.stopTest(self, test)
1204 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001205 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001206 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001207 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001208 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001209 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001210 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001211 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001212
1213 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001214
Damjan Marionf56b77a2016-10-03 19:44:57 +02001215 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001216 """
1217 Print errors from running the test case
1218 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001219 self.stream.writeln()
1220 self.printErrorList('ERROR', self.errors)
1221 self.printErrorList('FAIL', self.failures)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001222
Damjan Marionf56b77a2016-10-03 19:44:57 +02001223 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001224 """
1225 Print error list to the output stream together with error type
1226 and test case description.
1227
1228 :param flavour: error type
1229 :param errors: iterable errors
1230
1231 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001232 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001233 self.stream.writeln(double_line_delim)
1234 self.stream.writeln("%s: %s" %
1235 (flavour, self.getDescription(test)))
1236 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001237 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001238
1239
Damjan Marionf56b77a2016-10-03 19:44:57 +02001240class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001241 """
Klement Sekera104543f2017-02-03 07:29:43 +01001242 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001243 """
1244 @property
1245 def resultclass(self):
1246 """Class maintaining the results of the tests"""
1247 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001248
juraj.linkes184870a2018-07-16 14:22:01 +02001249 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001250 result_pipe=None, failfast=False, buffer=False,
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001251 resultclass=None):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001252
Klement Sekera7a161da2017-01-17 13:42:48 +01001253 # ignore stream setting here, use hard-coded stdout to be in sync
1254 # with prints from VppTestCase methods ...
1255 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1256 verbosity, failfast, buffer,
1257 resultclass)
Klement Sekera909a6a12017-08-08 04:33:53 +02001258 reporter = KeepAliveReporter()
Klement Sekeradf2b9802017-10-05 10:26:03 +02001259 reporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001260
juraj.linkescae64f82018-09-19 15:01:47 +02001261 VppTestResult.test_framework_result_pipe = result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001262
Damjan Marionf56b77a2016-10-03 19:44:57 +02001263 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001264 """
1265 Run the tests
1266
1267 :param test:
1268
1269 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001270 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001271
1272 result = super(VppTestRunner, self).run(test)
1273 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001274
1275
1276class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001277 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001278 self.logger = logger
1279 self.args = args
1280 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001281 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001282 super(Worker, self).__init__()
1283
1284 def run(self):
1285 executable = self.args[0]
1286 self.logger.debug("Running executable w/args `%s'" % self.args)
1287 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001288 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001289 env["CK_LOG_FILE_NAME"] = "-"
1290 self.process = subprocess.Popen(
1291 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1292 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1293 out, err = self.process.communicate()
1294 self.logger.debug("Finished running `%s'" % executable)
1295 self.logger.info("Return code is `%s'" % self.process.returncode)
1296 self.logger.info(single_line_delim)
1297 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1298 self.logger.info(single_line_delim)
1299 self.logger.info(out)
1300 self.logger.info(single_line_delim)
1301 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1302 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001303 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001304 self.logger.info(single_line_delim)
1305 self.result = self.process.returncode