blob: 06876a1799b1c8529f3d82dcd238f49786243c86 [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
154
155 @property
156 def pipe(self):
157 return self._pipe
158
159 @pipe.setter
160 def pipe(self, pipe):
161 if hasattr(self, '_pipe'):
162 raise Exception("Internal error - pipe should only be set once.")
163 self._pipe = pipe
164
juraj.linkes40dd73b2018-09-21 13:55:16 +0200165 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200166 """
167 Write current test tmpdir & desc to keep-alive pipe to signal liveness
168 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200169 if self.pipe is None:
170 # if not running forked..
171 return
172
Klement Sekera909a6a12017-08-08 04:33:53 +0200173 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200174 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200175 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200176 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200177
Dave Wallacee2efd122017-09-30 22:04:21 -0400178 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200179
180
Damjan Marionf56b77a2016-10-03 19:44:57 +0200181class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100182 """This subclass is a base class for VPP test cases that are implemented as
183 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200184 """
185
186 @property
187 def packet_infos(self):
188 """List of packet infos"""
189 return self._packet_infos
190
Klement Sekeradab231a2016-12-21 08:50:14 +0100191 @classmethod
192 def get_packet_count_for_if_idx(cls, dst_if_index):
193 """Get the number of packet info for specified destination if index"""
194 if dst_if_index in cls._packet_count_for_dst_if_idx:
195 return cls._packet_count_for_dst_if_idx[dst_if_index]
196 else:
197 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200198
199 @classmethod
200 def instance(cls):
201 """Return the instance of this testcase"""
202 return cls.test_instance
203
Damjan Marionf56b77a2016-10-03 19:44:57 +0200204 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200205 def set_debug_flags(cls, d):
206 cls.debug_core = False
207 cls.debug_gdb = False
208 cls.debug_gdbserver = False
209 if d is None:
210 return
211 dl = d.lower()
212 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200213 cls.debug_core = True
214 elif dl == "gdb":
215 cls.debug_gdb = True
216 elif dl == "gdbserver":
217 cls.debug_gdbserver = True
218 else:
219 raise Exception("Unrecognized DEBUG option: '%s'" % d)
220
221 @classmethod
juraj.linkes184870a2018-07-16 14:22:01 +0200222 def get_least_used_cpu(self):
223 cpu_usage_list = [set(range(psutil.cpu_count()))]
224 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
225 if 'vpp_main' == p.info['name']]
226 for vpp_process in vpp_processes:
227 for cpu_usage_set in cpu_usage_list:
228 try:
229 cpu_num = vpp_process.cpu_num()
230 if cpu_num in cpu_usage_set:
231 cpu_usage_set_index = cpu_usage_list.index(
232 cpu_usage_set)
233 if cpu_usage_set_index == len(cpu_usage_list) - 1:
234 cpu_usage_list.append({cpu_num})
235 else:
236 cpu_usage_list[cpu_usage_set_index + 1].add(
237 cpu_num)
238 cpu_usage_set.remove(cpu_num)
239 break
240 except psutil.NoSuchProcess:
241 pass
242
243 for cpu_usage_set in cpu_usage_list:
244 if len(cpu_usage_set) > 0:
245 min_usage_set = cpu_usage_set
246 break
247
248 return random.choice(tuple(min_usage_set))
249
juraj.linkes40dd73b2018-09-21 13:55:16 +0200250 @staticmethod
251 def print_header(cls):
252 if not hasattr(cls, '_header_printed'):
253 print(double_line_delim)
254 print(colorize(getdoc(cls).splitlines()[0], GREEN))
255 print(double_line_delim)
256 cls._header_printed = True
257
juraj.linkes184870a2018-07-16 14:22:01 +0200258 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200259 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200260 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100261 s = os.getenv("STEP", "n")
262 cls.step = True if s.lower() in ("y", "yes", "1") else False
263 d = os.getenv("DEBUG", None)
264 c = os.getenv("CACHE_OUTPUT", "1")
265 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200266 cls.set_debug_flags(d)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200267 cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp")
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100268 cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100269 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
270 plugin_path = None
271 if cls.plugin_path is not None:
272 if cls.extern_plugin_path is not None:
273 plugin_path = "%s:%s" % (
274 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100275 else:
276 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100277 elif cls.extern_plugin_path is not None:
278 plugin_path = cls.extern_plugin_path
Klement Sekera01bbbe92016-11-02 09:25:05 +0100279 debug_cli = ""
280 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
281 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100282 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100283 size = os.getenv("COREDUMP_SIZE")
284 if size is not None:
285 coredump_size = "coredump-size %s" % size
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100286 if coredump_size is None:
Dave Wallacee2efd122017-09-30 22:04:21 -0400287 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200288
289 cpu_core_number = cls.get_least_used_cpu()
290
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100291 cls.vpp_cmdline = [cls.vpp_bin, "unix",
Dave Wallacee2efd122017-09-30 22:04:21 -0400292 "{", "nodaemon", debug_cli, "full-coredump",
Jakub Grajciar99743912018-10-09 12:28:21 +0200293 coredump_size, "runtime-dir", cls.tempdir, "}",
294 "api-trace", "{", "on", "}", "api-segment", "{",
295 "prefix", cls.shm_prefix, "}", "cpu", "{",
296 "main-core", str(cpu_core_number), "}", "statseg",
297 "{", "socket-name", cls.stats_sock, "}", "plugins",
298 "{", "plugin", "dpdk_plugin.so", "{", "disable",
299 "}", "plugin", "unittest_plugin.so", "{", "enable",
300 "}", "}", ]
Klement Sekera47e275b2017-03-21 08:21:25 +0100301 if plugin_path is not None:
302 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100303 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
304 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200305
306 @classmethod
307 def wait_for_enter(cls):
308 if cls.debug_gdbserver:
309 print(double_line_delim)
310 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
311 elif cls.debug_gdb:
312 print(double_line_delim)
313 print("Spawned VPP with PID: %d" % cls.vpp.pid)
314 else:
315 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
316 return
317 print(single_line_delim)
318 print("You can debug the VPP using e.g.:")
319 if cls.debug_gdbserver:
320 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
321 print("Now is the time to attach a gdb by running the above "
322 "command, set up breakpoints etc. and then resume VPP from "
323 "within gdb by issuing the 'continue' command")
324 elif cls.debug_gdb:
325 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
326 print("Now is the time to attach a gdb by running the above "
327 "command and set up breakpoints etc.")
328 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100329 raw_input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200330
331 @classmethod
332 def run_vpp(cls):
333 cmdline = cls.vpp_cmdline
334
335 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100336 gdbserver = '/usr/bin/gdbserver'
337 if not os.path.isfile(gdbserver) or \
338 not os.access(gdbserver, os.X_OK):
339 raise Exception("gdbserver binary '%s' does not exist or is "
340 "not executable" % gdbserver)
341
342 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200343 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
344
Klement Sekera931be3a2016-11-03 05:36:01 +0100345 try:
346 cls.vpp = subprocess.Popen(cmdline,
347 stdout=subprocess.PIPE,
348 stderr=subprocess.PIPE,
349 bufsize=1)
350 except Exception as e:
351 cls.logger.critical("Couldn't start vpp: %s" % e)
352 raise
353
Klement Sekera277b89c2016-10-28 13:20:27 +0200354 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100355
Damjan Marionf56b77a2016-10-03 19:44:57 +0200356 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200357 def wait_for_stats_socket(cls):
358 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800359 ok = False
360 while time.time() < deadline or \
361 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200362 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800363 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200364 break
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800365 time.sleep(0.8)
366 if not ok:
367 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200368
369 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200370 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200371 """
372 Perform class setup before running the testcase
373 Remove shared memory files, start vpp and connect the vpp-api
374 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100375 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100376 random.seed()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200377 cls.print_header(cls)
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100378 cls.logger = get_logger(cls.__name__)
379 if hasattr(cls, 'parallel_handler'):
380 cls.logger.addHandler(cls.parallel_handler)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200381 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200382 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200383 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200384 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
385 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100386 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
387 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200388 cls.file_handler.setLevel(DEBUG)
389 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200390 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200391 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200392 cls.logger.info("Temporary dir is %s, shm prefix is %s",
393 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200394 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100395 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100396 cls._captures = []
397 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200398 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100399 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100400 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200401 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200402 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200403 # need to catch exceptions here because if we raise, then the cleanup
404 # doesn't get called and we might end with a zombie vpp
405 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200406 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200407 cls.reporter.send_keep_alive(cls, 'setUpClass')
408 VppTestResult.current_test_case_info = TestCaseInfo(
409 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100410 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100411 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100412 cls.pump_thread_stop_flag = Event()
413 cls.pump_thread_wakeup_pipe = os.pipe()
414 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100415 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100416 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200417 if cls.debug_gdb or cls.debug_gdbserver:
418 read_timeout = 0
419 else:
420 read_timeout = 5
421 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
422 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100423 if cls.step:
424 hook = StepHook(cls)
425 else:
426 hook = PollHook(cls)
427 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200428 cls.wait_for_stats_socket()
429 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200430 try:
431 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100432 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200433 cls.vpp_startup_failed = True
434 cls.logger.critical(
435 "VPP died shortly after startup, check the"
436 " output to standard error for possible cause")
437 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100438 try:
439 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100440 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100441 try:
442 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100443 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100444 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100445 if cls.debug_gdbserver:
446 print(colorize("You're running VPP inside gdbserver but "
447 "VPP-API connection failed, did you forget "
448 "to 'continue' VPP from within gdb?", RED))
449 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100450 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100451 try:
452 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100453 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100454 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100455 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200456
Damjan Marionf56b77a2016-10-03 19:44:57 +0200457 @classmethod
458 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200459 """
460 Disconnect vpp-api, kill vpp and cleanup shared memory files
461 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200462 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
463 cls.vpp.poll()
464 if cls.vpp.returncode is None:
465 print(double_line_delim)
466 print("VPP or GDB server is still running")
467 print(single_line_delim)
Klement Sekerae1ace192018-03-23 21:54:12 +0100468 raw_input("When done debugging, press ENTER to kill the "
469 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200470
juraj.linkes184870a2018-07-16 14:22:01 +0200471 # first signal that we want to stop the pump thread, then wake it up
472 if hasattr(cls, 'pump_thread_stop_flag'):
473 cls.pump_thread_stop_flag.set()
474 if hasattr(cls, 'pump_thread_wakeup_pipe'):
475 os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100476 if hasattr(cls, 'pump_thread'):
477 cls.logger.debug("Waiting for pump thread to stop")
478 cls.pump_thread.join()
479 if hasattr(cls, 'vpp_stderr_reader_thread'):
480 cls.logger.debug("Waiting for stdderr pump to stop")
481 cls.vpp_stderr_reader_thread.join()
482
Klement Sekeraf62ae122016-10-11 11:47:09 +0200483 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100484 if hasattr(cls, 'vapi'):
485 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100486 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200487 cls.vpp.poll()
488 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100489 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200490 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100491 cls.logger.debug("Waiting for vpp to die")
492 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200493 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200494
Klement Sekera3747c752017-04-10 06:30:17 +0200495 if cls.vpp_startup_failed:
496 stdout_log = cls.logger.info
497 stderr_log = cls.logger.critical
498 else:
499 stdout_log = cls.logger.info
500 stderr_log = cls.logger.info
501
Klement Sekerae4504c62016-12-08 10:16:41 +0100502 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200503 stdout_log(single_line_delim)
504 stdout_log('VPP output to stdout while running %s:', cls.__name__)
505 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100506 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200507 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
508 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200509 stdout_log('\n%s', vpp_output)
510 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200511
Klement Sekerae4504c62016-12-08 10:16:41 +0100512 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200513 stderr_log(single_line_delim)
514 stderr_log('VPP output to stderr while running %s:', cls.__name__)
515 stderr_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100516 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200517 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
518 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200519 stderr_log('\n%s', vpp_output)
520 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200521
Damjan Marionf56b77a2016-10-03 19:44:57 +0200522 @classmethod
523 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200524 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200525 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200526 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200527 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100528 cls.reset_packet_infos()
529 if debug_framework:
530 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200531
Damjan Marionf56b77a2016-10-03 19:44:57 +0200532 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200533 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100534 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
535 (self.__class__.__name__, self._testMethodName,
536 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200537 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200538 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700539 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200540 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200541 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200542 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800543 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100544 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500545 # Save/Dump VPP api trace log
546 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
547 tmp_api_trace = "/tmp/%s" % api_trace
548 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
549 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
550 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
551 vpp_api_trace_log))
552 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500553 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500554 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100555 else:
556 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200557
Damjan Marionf56b77a2016-10-03 19:44:57 +0200558 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200559 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200560 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100561 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
562 (self.__class__.__name__, self._testMethodName,
563 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100564 if self.vpp_dead:
565 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100566 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100567 self.vpp_stdout_deque.append(
568 "--- test setUp() for %s.%s(%s) starts here ---\n" %
569 (self.__class__.__name__, self._testMethodName,
570 self._testMethodDoc))
571 self.vpp_stderr_deque.append(
572 "--- test setUp() for %s.%s(%s) starts here ---\n" %
573 (self.__class__.__name__, self._testMethodName,
574 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200575 self.vapi.cli("clear trace")
576 # store the test instance inside the test class - so that objects
577 # holding the class can access instance methods (like assertEqual)
578 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200579
Damjan Marionf56b77a2016-10-03 19:44:57 +0200580 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200581 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200582 """
583 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200584
Klement Sekera75e7d132017-09-20 08:26:30 +0200585 :param interfaces: iterable interface indexes (if None,
586 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200587
Klement Sekeraf62ae122016-10-11 11:47:09 +0200588 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200589 if interfaces is None:
590 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200591 for i in interfaces:
592 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200593
Damjan Marionf56b77a2016-10-03 19:44:57 +0200594 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100595 def register_capture(cls, cap_name):
596 """ Register a capture in the testclass """
597 # add to the list of captures with current timestamp
598 cls._captures.append((time.time(), cap_name))
599 # filter out from zombies
600 cls._zombie_captures = [(stamp, name)
601 for (stamp, name) in cls._zombie_captures
602 if name != cap_name]
603
604 @classmethod
605 def pg_start(cls):
606 """ Remove any zombie captures and enable the packet generator """
607 # how long before capture is allowed to be deleted - otherwise vpp
608 # crashes - 100ms seems enough (this shouldn't be needed at all)
609 capture_ttl = 0.1
610 now = time.time()
611 for stamp, cap_name in cls._zombie_captures:
612 wait = stamp + capture_ttl - now
613 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100614 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100615 now = time.time()
616 cls.logger.debug("Removing zombie capture %s" % cap_name)
617 cls.vapi.cli('packet-generator delete %s' % cap_name)
618
Klement Sekeraf62ae122016-10-11 11:47:09 +0200619 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
620 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100621 cls._zombie_captures = cls._captures
622 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200623
Damjan Marionf56b77a2016-10-03 19:44:57 +0200624 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200625 def create_pg_interfaces(cls, interfaces):
626 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100627 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200628
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100629 :param interfaces: iterable indexes of the interfaces.
630 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200631
Klement Sekeraf62ae122016-10-11 11:47:09 +0200632 """
633 result = []
634 for i in interfaces:
635 intf = VppPGInterface(cls, i)
636 setattr(cls, intf.name, intf)
637 result.append(intf)
638 cls.pg_interfaces = result
639 return result
640
Matej Klotton0178d522016-11-04 11:11:44 +0100641 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200642 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100643 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100644 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100645
Klement Sekerab9ef2732018-06-24 22:49:33 +0200646 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100647 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100648 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200649 result = [VppLoInterface(cls) for i in range(count)]
650 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100651 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100652 cls.lo_interfaces = result
653 return result
654
Damjan Marionf56b77a2016-10-03 19:44:57 +0200655 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200656 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200657 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200658 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200659 NOTE: Currently works only when Raw layer is present.
660
661 :param packet: packet
662 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200663 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200664
665 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200666 packet_len = len(packet) + 4
667 extend = size - packet_len
668 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200669 num = (extend / len(padding)) + 1
670 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200671
Klement Sekeradab231a2016-12-21 08:50:14 +0100672 @classmethod
673 def reset_packet_infos(cls):
674 """ Reset the list of packet info objects and packet counts to zero """
675 cls._packet_infos = {}
676 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200677
Klement Sekeradab231a2016-12-21 08:50:14 +0100678 @classmethod
679 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200680 """
681 Create packet info object containing the source and destination indexes
682 and add it to the testcase's packet info list
683
Klement Sekeradab231a2016-12-21 08:50:14 +0100684 :param VppInterface src_if: source interface
685 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200686
687 :returns: _PacketInfo object
688
689 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200690 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100691 info.index = len(cls._packet_infos)
692 info.src = src_if.sw_if_index
693 info.dst = dst_if.sw_if_index
694 if isinstance(dst_if, VppSubInterface):
695 dst_idx = dst_if.parent.sw_if_index
696 else:
697 dst_idx = dst_if.sw_if_index
698 if dst_idx in cls._packet_count_for_dst_if_idx:
699 cls._packet_count_for_dst_if_idx[dst_idx] += 1
700 else:
701 cls._packet_count_for_dst_if_idx[dst_idx] = 1
702 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200703 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200704
Damjan Marionf56b77a2016-10-03 19:44:57 +0200705 @staticmethod
706 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200707 """
708 Convert _PacketInfo object to packet payload
709
710 :param info: _PacketInfo object
711
712 :returns: string containing serialized data from packet info
713 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100714 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
715 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200716
Damjan Marionf56b77a2016-10-03 19:44:57 +0200717 @staticmethod
718 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200719 """
720 Convert packet payload to _PacketInfo object
721
722 :param payload: packet payload
723
724 :returns: _PacketInfo object containing de-serialized data from payload
725
726 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727 numbers = payload.split()
728 info = _PacketInfo()
729 info.index = int(numbers[0])
730 info.src = int(numbers[1])
731 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100732 info.ip = int(numbers[3])
733 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200734 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200735
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200737 """
738 Iterate over the packet info list stored in the testcase
739 Start iteration with first element if info is None
740 Continue based on index in info if info is specified
741
742 :param info: info or None
743 :returns: next info in list or None if no more infos
744 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200745 if info is None:
746 next_index = 0
747 else:
748 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100749 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200750 return None
751 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100752 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200753
Klement Sekeraf62ae122016-10-11 11:47:09 +0200754 def get_next_packet_info_for_interface(self, src_index, info):
755 """
756 Search the packet info list for the next packet info with same source
757 interface index
758
759 :param src_index: source interface index to search for
760 :param info: packet info - where to start the search
761 :returns: packet info or None
762
763 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200764 while True:
765 info = self.get_next_packet_info(info)
766 if info is None:
767 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200768 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200770
Klement Sekeraf62ae122016-10-11 11:47:09 +0200771 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
772 """
773 Search the packet info list for the next packet info with same source
774 and destination interface indexes
775
776 :param src_index: source interface index to search for
777 :param dst_index: destination interface index to search for
778 :param info: packet info - where to start the search
779 :returns: packet info or None
780
781 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200782 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200783 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784 if info is None:
785 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200786 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200787 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200788
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200789 def assert_equal(self, real_value, expected_value, name_or_class=None):
790 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100791 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200792 return
793 try:
794 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
795 msg = msg % (getdoc(name_or_class).strip(),
796 real_value, str(name_or_class(real_value)),
797 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100798 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200799 msg = "Invalid %s: %s does not match expected value %s" % (
800 name_or_class, real_value, expected_value)
801
802 self.assertEqual(real_value, expected_value, msg)
803
Klement Sekerab17dd962017-01-09 07:43:48 +0100804 def assert_in_range(self,
805 real_value,
806 expected_min,
807 expected_max,
808 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200809 if name is None:
810 msg = None
811 else:
812 msg = "Invalid %s: %s out of range <%s,%s>" % (
813 name, real_value, expected_min, expected_max)
814 self.assertTrue(expected_min <= real_value <= expected_max, msg)
815
Klement Sekerad81ae412018-05-16 10:52:54 +0200816 def assert_packet_checksums_valid(self, packet,
817 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200818 received = packet.__class__(str(packet))
819 self.logger.debug(
820 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200821 udp_layers = ['UDP', 'UDPerror']
822 checksum_fields = ['cksum', 'chksum']
823 checksums = []
824 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200825 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200826 while True:
827 layer = temp.getlayer(counter)
828 if layer:
829 for cf in checksum_fields:
830 if hasattr(layer, cf):
831 if ignore_zero_udp_checksums and \
832 0 == getattr(layer, cf) and \
833 layer.name in udp_layers:
834 continue
835 delattr(layer, cf)
836 checksums.append((counter, cf))
837 else:
838 break
839 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200840 if 0 == len(checksums):
841 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200842 temp = temp.__class__(str(temp))
843 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200844 calc_sum = getattr(temp[layer], cf)
845 self.assert_equal(
846 getattr(received[layer], cf), calc_sum,
847 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
848 self.logger.debug(
849 "Checksum field `%s` on `%s` layer has correct value `%s`" %
850 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200851
852 def assert_checksum_valid(self, received_packet, layer,
853 field_name='chksum',
854 ignore_zero_checksum=False):
855 """ Check checksum of received packet on given layer """
856 received_packet_checksum = getattr(received_packet[layer], field_name)
857 if ignore_zero_checksum and 0 == received_packet_checksum:
858 return
859 recalculated = received_packet.__class__(str(received_packet))
860 delattr(recalculated[layer], field_name)
861 recalculated = recalculated.__class__(str(recalculated))
862 self.assert_equal(received_packet_checksum,
863 getattr(recalculated[layer], field_name),
864 "packet checksum on layer: %s" % layer)
865
866 def assert_ip_checksum_valid(self, received_packet,
867 ignore_zero_checksum=False):
868 self.assert_checksum_valid(received_packet, 'IP',
869 ignore_zero_checksum=ignore_zero_checksum)
870
871 def assert_tcp_checksum_valid(self, received_packet,
872 ignore_zero_checksum=False):
873 self.assert_checksum_valid(received_packet, 'TCP',
874 ignore_zero_checksum=ignore_zero_checksum)
875
876 def assert_udp_checksum_valid(self, received_packet,
877 ignore_zero_checksum=True):
878 self.assert_checksum_valid(received_packet, 'UDP',
879 ignore_zero_checksum=ignore_zero_checksum)
880
881 def assert_embedded_icmp_checksum_valid(self, received_packet):
882 if received_packet.haslayer(IPerror):
883 self.assert_checksum_valid(received_packet, 'IPerror')
884 if received_packet.haslayer(TCPerror):
885 self.assert_checksum_valid(received_packet, 'TCPerror')
886 if received_packet.haslayer(UDPerror):
887 self.assert_checksum_valid(received_packet, 'UDPerror',
888 ignore_zero_checksum=True)
889 if received_packet.haslayer(ICMPerror):
890 self.assert_checksum_valid(received_packet, 'ICMPerror')
891
892 def assert_icmp_checksum_valid(self, received_packet):
893 self.assert_checksum_valid(received_packet, 'ICMP')
894 self.assert_embedded_icmp_checksum_valid(received_packet)
895
896 def assert_icmpv6_checksum_valid(self, pkt):
897 if pkt.haslayer(ICMPv6DestUnreach):
898 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
899 self.assert_embedded_icmp_checksum_valid(pkt)
900 if pkt.haslayer(ICMPv6EchoRequest):
901 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
902 if pkt.haslayer(ICMPv6EchoReply):
903 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
904
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100905 def assert_packet_counter_equal(self, counter, expected_value):
906 counters = self.vapi.cli("sh errors").split('\n')
907 counter_value = -1
908 for i in range(1, len(counters)-1):
909 results = counters[i].split()
910 if results[1] == counter:
911 counter_value = int(results[0])
912 break
913 self.assert_equal(counter_value, expected_value,
914 "packet counter `%s'" % counter)
915
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100916 @classmethod
917 def sleep(cls, timeout, remark=None):
918 if hasattr(cls, 'logger'):
Klement Sekera3cfa5582017-04-19 07:10:58 +0000919 cls.logger.debug("Starting sleep for %ss (%s)" % (timeout, remark))
920 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100921 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000922 after = time.time()
923 if after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200924 cls.logger.error("unexpected time.sleep() result - "
925 "slept for %ss instead of ~%ss!" % (
926 after - before, timeout))
Klement Sekera3cfa5582017-04-19 07:10:58 +0000927 if hasattr(cls, 'logger'):
928 cls.logger.debug(
929 "Finished sleep (%s) - slept %ss (wanted %ss)" % (
930 remark, after - before, timeout))
Klement Sekeraa57a9702017-02-02 06:58:07 +0100931
Neale Ranns947ea622018-06-07 23:48:20 -0700932 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800933 self.vapi.cli("clear trace")
934 intf.add_stream(pkts)
935 self.pg_enable_capture(self.pg_interfaces)
936 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700937 if not timeout:
938 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800939 for i in self.pg_interfaces:
940 i.get_capture(0, timeout=timeout)
941 i.assert_nothing_captured(remark=remark)
942 timeout = 0.1
943
944 def send_and_expect(self, input, pkts, output):
945 self.vapi.cli("clear trace")
946 input.add_stream(pkts)
947 self.pg_enable_capture(self.pg_interfaces)
948 self.pg_start()
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700949 if isinstance(object, (list,)):
950 rx = []
951 for o in output:
952 rx.append(output.get_capture(len(pkts)))
953 else:
954 rx = output.get_capture(len(pkts))
955 return rx
956
957 def send_and_expect_only(self, input, pkts, output, timeout=None):
958 self.vapi.cli("clear trace")
959 input.add_stream(pkts)
960 self.pg_enable_capture(self.pg_interfaces)
961 self.pg_start()
962 if isinstance(object, (list,)):
963 outputs = output
964 rx = []
965 for o in outputs:
966 rx.append(output.get_capture(len(pkts)))
967 else:
968 rx = output.get_capture(len(pkts))
969 outputs = [output]
970 if not timeout:
971 timeout = 1
972 for i in self.pg_interfaces:
973 if i not in outputs:
974 i.get_capture(0, timeout=timeout)
975 i.assert_nothing_captured()
976 timeout = 0.1
977
Neale Ranns52fae862018-01-08 04:41:42 -0800978 return rx
979
Damjan Marionf56b77a2016-10-03 19:44:57 +0200980
juraj.linkes184870a2018-07-16 14:22:01 +0200981def get_testcase_doc_name(test):
982 return getdoc(test.__class__).splitlines()[0]
983
984
985def get_test_description(descriptions, test):
juraj.linkes184870a2018-07-16 14:22:01 +0200986 short_description = test.shortDescription()
987 if descriptions and short_description:
988 return short_description
989 else:
990 return str(test)
991
992
juraj.linkes40dd73b2018-09-21 13:55:16 +0200993class TestCaseInfo(object):
994 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
995 self.logger = logger
996 self.tempdir = tempdir
997 self.vpp_pid = vpp_pid
998 self.vpp_bin_path = vpp_bin_path
999 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001000
1001
Damjan Marionf56b77a2016-10-03 19:44:57 +02001002class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001003 """
1004 @property result_string
1005 String variable to store the test case result string.
1006 @property errors
1007 List variable containing 2-tuples of TestCase instances and strings
1008 holding formatted tracebacks. Each tuple represents a test which
1009 raised an unexpected exception.
1010 @property failures
1011 List variable containing 2-tuples of TestCase instances and strings
1012 holding formatted tracebacks. Each tuple represents a test where
1013 a failure was explicitly signalled using the TestCase.assert*()
1014 methods.
1015 """
1016
juraj.linkes40dd73b2018-09-21 13:55:16 +02001017 failed_test_cases_info = set()
1018 core_crash_test_cases_info = set()
1019 current_test_case_info = None
1020
Damjan Marionf56b77a2016-10-03 19:44:57 +02001021 def __init__(self, stream, descriptions, verbosity):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001022 """
Klement Sekerada505f62017-01-04 12:58:53 +01001023 :param stream File descriptor to store where to report test results.
1024 Set to the standard error stream by default.
1025 :param descriptions Boolean variable to store information if to use
1026 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001027 :param verbosity Integer variable to store required verbosity level.
1028 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001029 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
1030 self.stream = stream
1031 self.descriptions = descriptions
1032 self.verbosity = verbosity
1033 self.result_string = None
Damjan Marionf56b77a2016-10-03 19:44:57 +02001034
Damjan Marionf56b77a2016-10-03 19:44:57 +02001035 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001036 """
1037 Record a test succeeded result
1038
1039 :param test:
1040
1041 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001042 if self.current_test_case_info:
1043 self.current_test_case_info.logger.debug(
1044 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1045 test._testMethodName,
1046 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001047 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001048 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001049
juraj.linkescae64f82018-09-19 15:01:47 +02001050 self.send_result_through_pipe(test, PASS)
1051
Klement Sekeraf62ae122016-10-11 11:47:09 +02001052 def addSkip(self, test, reason):
1053 """
1054 Record a test skipped.
1055
1056 :param test:
1057 :param reason:
1058
1059 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001060 if self.current_test_case_info:
1061 self.current_test_case_info.logger.debug(
1062 "--- addSkip() %s.%s(%s) called, reason is %s" %
1063 (test.__class__.__name__, test._testMethodName,
1064 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001065 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001066 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001067
juraj.linkescae64f82018-09-19 15:01:47 +02001068 self.send_result_through_pipe(test, SKIP)
1069
juraj.linkes40dd73b2018-09-21 13:55:16 +02001070 def symlink_failed(self):
1071 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001072 try:
1073 failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001074 link_path = os.path.join(
1075 failed_dir,
1076 '%s-FAILED' %
1077 os.path.basename(self.current_test_case_info.tempdir))
1078 if self.current_test_case_info.logger:
1079 self.current_test_case_info.logger.debug(
1080 "creating a link to the failed test")
1081 self.current_test_case_info.logger.debug(
1082 "os.symlink(%s, %s)" %
1083 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001084 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001085 if self.current_test_case_info.logger:
1086 self.current_test_case_info.logger.debug(
1087 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001088 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001089 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001090
Klement Sekeraf413bef2017-08-15 07:09:02 +02001091 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001092 if self.current_test_case_info.logger:
1093 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001094
juraj.linkescae64f82018-09-19 15:01:47 +02001095 def send_result_through_pipe(self, test, result):
1096 if hasattr(self, 'test_framework_result_pipe'):
1097 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001098 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001099 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001100
juraj.linkes40dd73b2018-09-21 13:55:16 +02001101 def log_error(self, test, err, fn_name):
1102 if self.current_test_case_info:
1103 if isinstance(test, unittest.suite._ErrorHolder):
1104 test_name = test.description
1105 else:
1106 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1107 test._testMethodName,
1108 test._testMethodDoc)
1109 self.current_test_case_info.logger.debug(
1110 "--- %s() %s called, err is %s" %
1111 (fn_name, test_name, err))
1112 self.current_test_case_info.logger.debug(
1113 "formatted exception is:\n%s" %
1114 "".join(format_exception(*err)))
1115
1116 def add_error(self, test, err, unittest_fn, error_type):
1117 if error_type == FAIL:
1118 self.log_error(test, err, 'addFailure')
1119 error_type_str = colorize("FAIL", RED)
1120 elif error_type == ERROR:
1121 self.log_error(test, err, 'addError')
1122 error_type_str = colorize("ERROR", RED)
1123 else:
1124 raise Exception('Error type %s cannot be used to record an '
1125 'error or a failure' % error_type)
1126
1127 unittest_fn(self, test, err)
1128 if self.current_test_case_info:
1129 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1130 (error_type_str,
1131 self.current_test_case_info.tempdir)
1132 self.symlink_failed()
1133 self.failed_test_cases_info.add(self.current_test_case_info)
1134 if is_core_present(self.current_test_case_info.tempdir):
1135 if not self.current_test_case_info.core_crash_test:
1136 if isinstance(test, unittest.suite._ErrorHolder):
1137 test_name = str(test)
1138 else:
1139 test_name = "'{}' ({})".format(
1140 get_testcase_doc_name(test), test.id())
1141 self.current_test_case_info.core_crash_test = test_name
1142 self.core_crash_test_cases_info.add(
1143 self.current_test_case_info)
1144 else:
1145 self.result_string = '%s [no temp dir]' % error_type_str
1146
1147 self.send_result_through_pipe(test, error_type)
1148
Damjan Marionf56b77a2016-10-03 19:44:57 +02001149 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001150 """
1151 Record a test failed result
1152
1153 :param test:
1154 :param err: error message
1155
1156 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001157 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001158
Damjan Marionf56b77a2016-10-03 19:44:57 +02001159 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001160 """
1161 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001162
Klement Sekeraf62ae122016-10-11 11:47:09 +02001163 :param test:
1164 :param err: error message
1165
1166 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001167 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001168
Damjan Marionf56b77a2016-10-03 19:44:57 +02001169 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001170 """
1171 Get test description
1172
1173 :param test:
1174 :returns: test description
1175
1176 """
juraj.linkes184870a2018-07-16 14:22:01 +02001177 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001178
Damjan Marionf56b77a2016-10-03 19:44:57 +02001179 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001180 """
1181 Start a test
1182
1183 :param test:
1184
1185 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001186 test.print_header(test.__class__)
1187
Damjan Marionf56b77a2016-10-03 19:44:57 +02001188 unittest.TestResult.startTest(self, test)
1189 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001190 self.stream.writeln(
1191 "Starting " + self.getDescription(test) + " ...")
1192 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001193
Damjan Marionf56b77a2016-10-03 19:44:57 +02001194 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001195 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001196 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001197
1198 :param test:
1199
1200 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001201 unittest.TestResult.stopTest(self, test)
1202 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001203 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001204 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001205 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001206 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001207 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001208 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001209 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001210
1211 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001212
Damjan Marionf56b77a2016-10-03 19:44:57 +02001213 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001214 """
1215 Print errors from running the test case
1216 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001217 self.stream.writeln()
1218 self.printErrorList('ERROR', self.errors)
1219 self.printErrorList('FAIL', self.failures)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001220
Damjan Marionf56b77a2016-10-03 19:44:57 +02001221 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001222 """
1223 Print error list to the output stream together with error type
1224 and test case description.
1225
1226 :param flavour: error type
1227 :param errors: iterable errors
1228
1229 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001230 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001231 self.stream.writeln(double_line_delim)
1232 self.stream.writeln("%s: %s" %
1233 (flavour, self.getDescription(test)))
1234 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001235 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001236
1237
Damjan Marionf56b77a2016-10-03 19:44:57 +02001238class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001239 """
Klement Sekera104543f2017-02-03 07:29:43 +01001240 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001241 """
1242 @property
1243 def resultclass(self):
1244 """Class maintaining the results of the tests"""
1245 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001246
juraj.linkes184870a2018-07-16 14:22:01 +02001247 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001248 result_pipe=None, failfast=False, buffer=False,
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001249 resultclass=None):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001250
Klement Sekera7a161da2017-01-17 13:42:48 +01001251 # ignore stream setting here, use hard-coded stdout to be in sync
1252 # with prints from VppTestCase methods ...
1253 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1254 verbosity, failfast, buffer,
1255 resultclass)
Klement Sekera909a6a12017-08-08 04:33:53 +02001256 reporter = KeepAliveReporter()
Klement Sekeradf2b9802017-10-05 10:26:03 +02001257 reporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001258
juraj.linkescae64f82018-09-19 15:01:47 +02001259 VppTestResult.test_framework_result_pipe = result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001260
Damjan Marionf56b77a2016-10-03 19:44:57 +02001261 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001262 """
1263 Run the tests
1264
1265 :param test:
1266
1267 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001268 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001269
1270 result = super(VppTestRunner, self).run(test)
1271 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001272
1273
1274class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001275 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001276 self.logger = logger
1277 self.args = args
1278 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001279 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001280 super(Worker, self).__init__()
1281
1282 def run(self):
1283 executable = self.args[0]
1284 self.logger.debug("Running executable w/args `%s'" % self.args)
1285 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001286 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001287 env["CK_LOG_FILE_NAME"] = "-"
1288 self.process = subprocess.Popen(
1289 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1290 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1291 out, err = self.process.communicate()
1292 self.logger.debug("Finished running `%s'" % executable)
1293 self.logger.info("Return code is `%s'" % self.process.returncode)
1294 self.logger.info(single_line_delim)
1295 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1296 self.logger.info(single_line_delim)
1297 self.logger.info(out)
1298 self.logger.info(single_line_delim)
1299 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1300 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001301 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001302 self.logger.info(single_line_delim)
1303 self.result = self.process.returncode