blob: e3cf68d4aa5e9c35e82237e85203afbb5cda4a2a [file] [log] [blame]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001#!/usr/bin/env python
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
4import gc
Paul Vinciguerra72f00042018-11-25 11:05:13 -08005import sys
Ole Trøan162989e2018-11-26 10:27:50 +00006import os
7import select
8import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +02009import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020010import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080011import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000012import random
13import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080014import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010015import platform
Ole Trøan162989e2018-11-26 10:27:50 +000016from collections import deque
17from threading import Thread, Event
18from inspect import getdoc, isclass
19from traceback import format_exception
20from logging import FileHandler, DEBUG, Formatter
21from scapy.packet import Raw
22from hook import StepHook, PollHook, VppDiedError
23from vpp_pg_interface import VppPGInterface
24from vpp_sub_interface import VppSubInterface
25from vpp_lo_interface import VppLoInterface
26from vpp_papi_provider import VppPapiProvider
27from vpp_papi.vpp_stats import VPPStats
28from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
29 get_logger, colorize
30from vpp_object import VppObjectRegistry
31from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020032from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
33from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
34from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080035
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010036if os.name == 'posix' and sys.version_info[0] < 3:
37 # using subprocess32 is recommended by python official documentation
38 # @ https://docs.python.org/2/library/subprocess.html
39 import subprocess32 as subprocess
40else:
41 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020042
juraj.linkescae64f82018-09-19 15:01:47 +020043PASS = 0
44FAIL = 1
45ERROR = 2
46SKIP = 3
47TEST_RUN = 4
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
Klement Sekeraf62ae122016-10-11 11:47:09 +020054"""
55 Test framework module.
56
57 The module provides a set of tools for constructing and running tests and
58 representing the results.
59"""
60
Klement Sekeraf62ae122016-10-11 11:47:09 +020061
Damjan Marionf56b77a2016-10-03 19:44:57 +020062class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020063 """Private class to create packet info object.
64
65 Help process information about the next packet.
66 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020067 """
Matej Klotton86d87c42016-11-11 11:38:55 +010068 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020069 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010070 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020071 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010072 #: Store the index of the destination packet generator interface
73 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020074 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010075 #: Store expected ip version
76 ip = -1
77 #: Store expected upper protocol
78 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010079 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020080 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020081
Matej Klotton16a14cd2016-12-07 15:09:13 +010082 def __eq__(self, other):
83 index = self.index == other.index
84 src = self.src == other.src
85 dst = self.dst == other.dst
86 data = self.data == other.data
87 return index and src and dst and data
88
Klement Sekeraf62ae122016-10-11 11:47:09 +020089
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010090def pump_output(testclass):
91 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +010092 stdout_fragment = ""
93 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -040094 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010095 readable = select.select([testclass.vpp.stdout.fileno(),
96 testclass.vpp.stderr.fileno(),
97 testclass.pump_thread_wakeup_pipe[0]],
98 [], [])[0]
99 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100100 read = os.read(testclass.vpp.stdout.fileno(), 102400)
101 if len(read) > 0:
102 split = read.splitlines(True)
103 if len(stdout_fragment) > 0:
104 split[0] = "%s%s" % (stdout_fragment, split[0])
105 if len(split) > 0 and split[-1].endswith("\n"):
106 limit = None
107 else:
108 limit = -1
109 stdout_fragment = split[-1]
110 testclass.vpp_stdout_deque.extend(split[:limit])
111 if not testclass.cache_vpp_output:
112 for line in split[:limit]:
113 testclass.logger.debug(
114 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100115 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100116 read = os.read(testclass.vpp.stderr.fileno(), 102400)
117 if len(read) > 0:
118 split = read.splitlines(True)
119 if len(stderr_fragment) > 0:
120 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan7f991832018-12-06 17:35:12 +0100121 if len(split) > 0 and split[-1].endswith(b"\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100122 limit = None
123 else:
124 limit = -1
125 stderr_fragment = split[-1]
126 testclass.vpp_stderr_deque.extend(split[:limit])
127 if not testclass.cache_vpp_output:
128 for line in split[:limit]:
129 testclass.logger.debug(
130 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800131 # ignoring the dummy pipe here intentionally - the
132 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200133
134
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800135def _is_skip_aarch64_set():
juraj.linkes68ebc832018-11-29 09:37:08 +0100136 return os.getenv('SKIP_AARCH64', 'n').lower() in ('yes', 'y', '1')
137
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800138is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100139
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800140
141def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100142 return platform.machine() == 'aarch64'
143
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800144is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100145
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800146
147def _running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100148 s = os.getenv("EXTENDED_TESTS", "n")
149 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100150
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800151running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100152
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800153
154def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100155 os_id = os.getenv("OS_ID", "")
156 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200157
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800158running_on_centos = _running_on_centos
159
Klement Sekerad3e671e2017-09-29 12:36:37 +0200160
Klement Sekera909a6a12017-08-08 04:33:53 +0200161class KeepAliveReporter(object):
162 """
163 Singleton object which reports test start to parent process
164 """
165 _shared_state = {}
166
167 def __init__(self):
168 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800169 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200170
171 @property
172 def pipe(self):
173 return self._pipe
174
175 @pipe.setter
176 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800177 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200178 raise Exception("Internal error - pipe should only be set once.")
179 self._pipe = pipe
180
juraj.linkes40dd73b2018-09-21 13:55:16 +0200181 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200182 """
183 Write current test tmpdir & desc to keep-alive pipe to signal liveness
184 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200185 if self.pipe is None:
186 # if not running forked..
187 return
188
Klement Sekera909a6a12017-08-08 04:33:53 +0200189 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200190 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200191 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200192 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200193
Dave Wallacee2efd122017-09-30 22:04:21 -0400194 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200195
196
Damjan Marionf56b77a2016-10-03 19:44:57 +0200197class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100198 """This subclass is a base class for VPP test cases that are implemented as
199 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200200 """
201
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100202 extra_vpp_punt_config = []
Klement Sekerab6d92d82018-12-03 12:24:21 +0100203 extra_vpp_plugin_config = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100204
Klement Sekeraf62ae122016-10-11 11:47:09 +0200205 @property
206 def packet_infos(self):
207 """List of packet infos"""
208 return self._packet_infos
209
Klement Sekeradab231a2016-12-21 08:50:14 +0100210 @classmethod
211 def get_packet_count_for_if_idx(cls, dst_if_index):
212 """Get the number of packet info for specified destination if index"""
213 if dst_if_index in cls._packet_count_for_dst_if_idx:
214 return cls._packet_count_for_dst_if_idx[dst_if_index]
215 else:
216 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200217
218 @classmethod
219 def instance(cls):
220 """Return the instance of this testcase"""
221 return cls.test_instance
222
Damjan Marionf56b77a2016-10-03 19:44:57 +0200223 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200224 def set_debug_flags(cls, d):
225 cls.debug_core = False
226 cls.debug_gdb = False
227 cls.debug_gdbserver = False
228 if d is None:
229 return
230 dl = d.lower()
231 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200232 cls.debug_core = True
233 elif dl == "gdb":
234 cls.debug_gdb = True
235 elif dl == "gdbserver":
236 cls.debug_gdbserver = True
237 else:
238 raise Exception("Unrecognized DEBUG option: '%s'" % d)
239
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800240 @staticmethod
241 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200242 cpu_usage_list = [set(range(psutil.cpu_count()))]
243 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
244 if 'vpp_main' == p.info['name']]
245 for vpp_process in vpp_processes:
246 for cpu_usage_set in cpu_usage_list:
247 try:
248 cpu_num = vpp_process.cpu_num()
249 if cpu_num in cpu_usage_set:
250 cpu_usage_set_index = cpu_usage_list.index(
251 cpu_usage_set)
252 if cpu_usage_set_index == len(cpu_usage_list) - 1:
253 cpu_usage_list.append({cpu_num})
254 else:
255 cpu_usage_list[cpu_usage_set_index + 1].add(
256 cpu_num)
257 cpu_usage_set.remove(cpu_num)
258 break
259 except psutil.NoSuchProcess:
260 pass
261
262 for cpu_usage_set in cpu_usage_list:
263 if len(cpu_usage_set) > 0:
264 min_usage_set = cpu_usage_set
265 break
266
267 return random.choice(tuple(min_usage_set))
268
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800269 @classmethod
juraj.linkes40dd73b2018-09-21 13:55:16 +0200270 def print_header(cls):
271 if not hasattr(cls, '_header_printed'):
272 print(double_line_delim)
273 print(colorize(getdoc(cls).splitlines()[0], GREEN))
274 print(double_line_delim)
275 cls._header_printed = True
276
juraj.linkes184870a2018-07-16 14:22:01 +0200277 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200278 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200279 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100280 s = os.getenv("STEP", "n")
281 cls.step = True if s.lower() in ("y", "yes", "1") else False
282 d = os.getenv("DEBUG", None)
283 c = os.getenv("CACHE_OUTPUT", "1")
284 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200285 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100286 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
287 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100288 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
289 plugin_path = None
290 if cls.plugin_path is not None:
291 if cls.extern_plugin_path is not None:
292 plugin_path = "%s:%s" % (
293 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100294 else:
295 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100296 elif cls.extern_plugin_path is not None:
297 plugin_path = cls.extern_plugin_path
Klement Sekera01bbbe92016-11-02 09:25:05 +0100298 debug_cli = ""
299 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
300 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100301 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100302 size = os.getenv("COREDUMP_SIZE")
303 if size is not None:
304 coredump_size = "coredump-size %s" % size
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100305 if coredump_size is None:
Dave Wallacee2efd122017-09-30 22:04:21 -0400306 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200307
308 cpu_core_number = cls.get_least_used_cpu()
309
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100310 cls.vpp_cmdline = [cls.vpp_bin, "unix",
Dave Wallacee2efd122017-09-30 22:04:21 -0400311 "{", "nodaemon", debug_cli, "full-coredump",
Jakub Grajciar99743912018-10-09 12:28:21 +0200312 coredump_size, "runtime-dir", cls.tempdir, "}",
313 "api-trace", "{", "on", "}", "api-segment", "{",
314 "prefix", cls.shm_prefix, "}", "cpu", "{",
315 "main-core", str(cpu_core_number), "}", "statseg",
316 "{", "socket-name", cls.stats_sock, "}", "plugins",
317 "{", "plugin", "dpdk_plugin.so", "{", "disable",
318 "}", "plugin", "unittest_plugin.so", "{", "enable",
Klement Sekerab6d92d82018-12-03 12:24:21 +0100319 "}"] + cls.extra_vpp_plugin_config + ["}", ]
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100320 if cls.extra_vpp_punt_config is not None:
321 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100322 if plugin_path is not None:
323 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100324 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
325 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200326
327 @classmethod
328 def wait_for_enter(cls):
329 if cls.debug_gdbserver:
330 print(double_line_delim)
331 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
332 elif cls.debug_gdb:
333 print(double_line_delim)
334 print("Spawned VPP with PID: %d" % cls.vpp.pid)
335 else:
336 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
337 return
338 print(single_line_delim)
339 print("You can debug the VPP using e.g.:")
340 if cls.debug_gdbserver:
341 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
342 print("Now is the time to attach a gdb by running the above "
343 "command, set up breakpoints etc. and then resume VPP from "
344 "within gdb by issuing the 'continue' command")
345 elif cls.debug_gdb:
346 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
347 print("Now is the time to attach a gdb by running the above "
348 "command and set up breakpoints etc.")
349 print(single_line_delim)
Ole Trøan162989e2018-11-26 10:27:50 +0000350 raw_input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200351
352 @classmethod
353 def run_vpp(cls):
354 cmdline = cls.vpp_cmdline
355
356 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100357 gdbserver = '/usr/bin/gdbserver'
358 if not os.path.isfile(gdbserver) or \
359 not os.access(gdbserver, os.X_OK):
360 raise Exception("gdbserver binary '%s' does not exist or is "
361 "not executable" % gdbserver)
362
363 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200364 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
365
Klement Sekera931be3a2016-11-03 05:36:01 +0100366 try:
367 cls.vpp = subprocess.Popen(cmdline,
368 stdout=subprocess.PIPE,
369 stderr=subprocess.PIPE,
370 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800371 except subprocess.CalledProcessError as e:
Klement Sekera931be3a2016-11-03 05:36:01 +0100372 cls.logger.critical("Couldn't start vpp: %s" % e)
373 raise
374
Klement Sekera277b89c2016-10-28 13:20:27 +0200375 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100376
Damjan Marionf56b77a2016-10-03 19:44:57 +0200377 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200378 def wait_for_stats_socket(cls):
379 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800380 ok = False
381 while time.time() < deadline or \
382 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200383 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800384 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200385 break
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800386 time.sleep(0.8)
387 if not ok:
388 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200389
390 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200391 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200392 """
393 Perform class setup before running the testcase
394 Remove shared memory files, start vpp and connect the vpp-api
395 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100396 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100397 random.seed()
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800398 cls.print_header()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100399 cls.logger = get_logger(cls.__name__)
400 if hasattr(cls, 'parallel_handler'):
401 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100402 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200403 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200404 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200405 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200406 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
407 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100408 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
409 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200410 cls.file_handler.setLevel(DEBUG)
411 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200412 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200413 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200414 cls.logger.info("Temporary dir is %s, shm prefix is %s",
415 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200416 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100417 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100418 cls._captures = []
419 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200420 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100421 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100422 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200423 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200424 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200425 # need to catch exceptions here because if we raise, then the cleanup
426 # doesn't get called and we might end with a zombie vpp
427 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200428 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200429 cls.reporter.send_keep_alive(cls, 'setUpClass')
430 VppTestResult.current_test_case_info = TestCaseInfo(
431 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100432 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100433 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100434 cls.pump_thread_stop_flag = Event()
435 cls.pump_thread_wakeup_pipe = os.pipe()
436 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100437 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100438 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200439 if cls.debug_gdb or cls.debug_gdbserver:
440 read_timeout = 0
441 else:
442 read_timeout = 5
443 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
444 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100445 if cls.step:
446 hook = StepHook(cls)
447 else:
448 hook = PollHook(cls)
449 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200450 cls.wait_for_stats_socket()
451 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200452 try:
453 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100454 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200455 cls.vpp_startup_failed = True
456 cls.logger.critical(
457 "VPP died shortly after startup, check the"
458 " output to standard error for possible cause")
459 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100460 try:
461 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100462 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100463 try:
464 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100465 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100466 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100467 if cls.debug_gdbserver:
468 print(colorize("You're running VPP inside gdbserver but "
469 "VPP-API connection failed, did you forget "
470 "to 'continue' VPP from within gdb?", RED))
471 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100472 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100473 try:
474 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100475 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100476 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100477 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200478
Damjan Marionf56b77a2016-10-03 19:44:57 +0200479 @classmethod
480 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200481 """
482 Disconnect vpp-api, kill vpp and cleanup shared memory files
483 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200484 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
485 cls.vpp.poll()
486 if cls.vpp.returncode is None:
487 print(double_line_delim)
488 print("VPP or GDB server is still running")
489 print(single_line_delim)
Ole Trøan162989e2018-11-26 10:27:50 +0000490 raw_input("When done debugging, press ENTER to kill the "
Klement Sekerae1ace192018-03-23 21:54:12 +0100491 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200492
juraj.linkes184870a2018-07-16 14:22:01 +0200493 # first signal that we want to stop the pump thread, then wake it up
494 if hasattr(cls, 'pump_thread_stop_flag'):
495 cls.pump_thread_stop_flag.set()
496 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100497 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100498 if hasattr(cls, 'pump_thread'):
499 cls.logger.debug("Waiting for pump thread to stop")
500 cls.pump_thread.join()
501 if hasattr(cls, 'vpp_stderr_reader_thread'):
502 cls.logger.debug("Waiting for stdderr pump to stop")
503 cls.vpp_stderr_reader_thread.join()
504
Klement Sekeraf62ae122016-10-11 11:47:09 +0200505 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100506 if hasattr(cls, 'vapi'):
507 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100508 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200509 cls.vpp.poll()
510 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100511 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200512 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100513 cls.logger.debug("Waiting for vpp to die")
514 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200515 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200516
Klement Sekera3747c752017-04-10 06:30:17 +0200517 if cls.vpp_startup_failed:
518 stdout_log = cls.logger.info
519 stderr_log = cls.logger.critical
520 else:
521 stdout_log = cls.logger.info
522 stderr_log = cls.logger.info
523
Klement Sekerae4504c62016-12-08 10:16:41 +0100524 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200525 stdout_log(single_line_delim)
526 stdout_log('VPP output to stdout while running %s:', cls.__name__)
527 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100528 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200529 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
530 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200531 stdout_log('\n%s', vpp_output)
532 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200533
Klement Sekerae4504c62016-12-08 10:16:41 +0100534 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200535 stderr_log(single_line_delim)
536 stderr_log('VPP output to stderr while running %s:', cls.__name__)
537 stderr_log(single_line_delim)
Ole Troan7f991832018-12-06 17:35:12 +0100538 vpp_output = "".join(str(cls.vpp_stderr_deque))
Klement Sekera027dbd52017-04-11 06:01:53 +0200539 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
540 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200541 stderr_log('\n%s', vpp_output)
542 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200543
Damjan Marionf56b77a2016-10-03 19:44:57 +0200544 @classmethod
545 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200546 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200547 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200548 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200549 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100550 cls.reset_packet_infos()
551 if debug_framework:
552 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200553
Damjan Marionf56b77a2016-10-03 19:44:57 +0200554 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200555 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100556 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
557 (self.__class__.__name__, self._testMethodName,
558 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200559 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200560 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700561 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200562 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200563 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200564 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800565 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100566 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500567 # Save/Dump VPP api trace log
568 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
569 tmp_api_trace = "/tmp/%s" % api_trace
570 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
571 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
572 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
573 vpp_api_trace_log))
574 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500575 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500576 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100577 else:
578 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200579
Damjan Marionf56b77a2016-10-03 19:44:57 +0200580 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200581 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200582 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100583 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
584 (self.__class__.__name__, self._testMethodName,
585 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100586 if self.vpp_dead:
587 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100588 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100589 self.vpp_stdout_deque.append(
590 "--- test setUp() for %s.%s(%s) starts here ---\n" %
591 (self.__class__.__name__, self._testMethodName,
592 self._testMethodDoc))
593 self.vpp_stderr_deque.append(
594 "--- test setUp() for %s.%s(%s) starts here ---\n" %
595 (self.__class__.__name__, self._testMethodName,
596 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200597 self.vapi.cli("clear trace")
598 # store the test instance inside the test class - so that objects
599 # holding the class can access instance methods (like assertEqual)
600 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200601
Damjan Marionf56b77a2016-10-03 19:44:57 +0200602 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200603 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200604 """
605 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200606
Klement Sekera75e7d132017-09-20 08:26:30 +0200607 :param interfaces: iterable interface indexes (if None,
608 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200609
Klement Sekeraf62ae122016-10-11 11:47:09 +0200610 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200611 if interfaces is None:
612 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200613 for i in interfaces:
614 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200615
Damjan Marionf56b77a2016-10-03 19:44:57 +0200616 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100617 def register_capture(cls, cap_name):
618 """ Register a capture in the testclass """
619 # add to the list of captures with current timestamp
620 cls._captures.append((time.time(), cap_name))
621 # filter out from zombies
622 cls._zombie_captures = [(stamp, name)
623 for (stamp, name) in cls._zombie_captures
624 if name != cap_name]
625
626 @classmethod
627 def pg_start(cls):
628 """ Remove any zombie captures and enable the packet generator """
629 # how long before capture is allowed to be deleted - otherwise vpp
630 # crashes - 100ms seems enough (this shouldn't be needed at all)
631 capture_ttl = 0.1
632 now = time.time()
633 for stamp, cap_name in cls._zombie_captures:
634 wait = stamp + capture_ttl - now
635 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100636 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100637 now = time.time()
638 cls.logger.debug("Removing zombie capture %s" % cap_name)
639 cls.vapi.cli('packet-generator delete %s' % cap_name)
640
Klement Sekeraf62ae122016-10-11 11:47:09 +0200641 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
642 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100643 cls._zombie_captures = cls._captures
644 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200645
Damjan Marionf56b77a2016-10-03 19:44:57 +0200646 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200647 def create_pg_interfaces(cls, interfaces):
648 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100649 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200650
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100651 :param interfaces: iterable indexes of the interfaces.
652 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200653
Klement Sekeraf62ae122016-10-11 11:47:09 +0200654 """
655 result = []
656 for i in interfaces:
657 intf = VppPGInterface(cls, i)
658 setattr(cls, intf.name, intf)
659 result.append(intf)
660 cls.pg_interfaces = result
661 return result
662
Matej Klotton0178d522016-11-04 11:11:44 +0100663 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200664 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100665 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100666 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100667
Klement Sekerab9ef2732018-06-24 22:49:33 +0200668 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100669 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100670 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200671 result = [VppLoInterface(cls) for i in range(count)]
672 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100673 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100674 cls.lo_interfaces = result
675 return result
676
Damjan Marionf56b77a2016-10-03 19:44:57 +0200677 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200678 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200679 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200680 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200681 NOTE: Currently works only when Raw layer is present.
682
683 :param packet: packet
684 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200685 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200686
687 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200688 packet_len = len(packet) + 4
689 extend = size - packet_len
690 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200691 num = (extend / len(padding)) + 1
692 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200693
Klement Sekeradab231a2016-12-21 08:50:14 +0100694 @classmethod
695 def reset_packet_infos(cls):
696 """ Reset the list of packet info objects and packet counts to zero """
697 cls._packet_infos = {}
698 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200699
Klement Sekeradab231a2016-12-21 08:50:14 +0100700 @classmethod
701 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200702 """
703 Create packet info object containing the source and destination indexes
704 and add it to the testcase's packet info list
705
Klement Sekeradab231a2016-12-21 08:50:14 +0100706 :param VppInterface src_if: source interface
707 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200708
709 :returns: _PacketInfo object
710
711 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200712 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100713 info.index = len(cls._packet_infos)
714 info.src = src_if.sw_if_index
715 info.dst = dst_if.sw_if_index
716 if isinstance(dst_if, VppSubInterface):
717 dst_idx = dst_if.parent.sw_if_index
718 else:
719 dst_idx = dst_if.sw_if_index
720 if dst_idx in cls._packet_count_for_dst_if_idx:
721 cls._packet_count_for_dst_if_idx[dst_idx] += 1
722 else:
723 cls._packet_count_for_dst_if_idx[dst_idx] = 1
724 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200725 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200726
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727 @staticmethod
728 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200729 """
730 Convert _PacketInfo object to packet payload
731
732 :param info: _PacketInfo object
733
734 :returns: string containing serialized data from packet info
735 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100736 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
737 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200738
Damjan Marionf56b77a2016-10-03 19:44:57 +0200739 @staticmethod
740 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200741 """
742 Convert packet payload to _PacketInfo object
743
744 :param payload: packet payload
745
746 :returns: _PacketInfo object containing de-serialized data from payload
747
748 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200749 numbers = payload.split()
750 info = _PacketInfo()
751 info.index = int(numbers[0])
752 info.src = int(numbers[1])
753 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100754 info.ip = int(numbers[3])
755 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200756 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200757
Damjan Marionf56b77a2016-10-03 19:44:57 +0200758 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200759 """
760 Iterate over the packet info list stored in the testcase
761 Start iteration with first element if info is None
762 Continue based on index in info if info is specified
763
764 :param info: info or None
765 :returns: next info in list or None if no more infos
766 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200767 if info is None:
768 next_index = 0
769 else:
770 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100771 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200772 return None
773 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100774 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200775
Klement Sekeraf62ae122016-10-11 11:47:09 +0200776 def get_next_packet_info_for_interface(self, src_index, info):
777 """
778 Search the packet info list for the next packet info with same source
779 interface index
780
781 :param src_index: source interface index to search for
782 :param info: packet info - where to start the search
783 :returns: packet info or None
784
785 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200786 while True:
787 info = self.get_next_packet_info(info)
788 if info is None:
789 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200790 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200791 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200792
Klement Sekeraf62ae122016-10-11 11:47:09 +0200793 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
794 """
795 Search the packet info list for the next packet info with same source
796 and destination interface indexes
797
798 :param src_index: source interface index to search for
799 :param dst_index: destination interface index to search for
800 :param info: packet info - where to start the search
801 :returns: packet info or None
802
803 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200804 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200805 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200806 if info is None:
807 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200808 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200809 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200810
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200811 def assert_equal(self, real_value, expected_value, name_or_class=None):
812 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100813 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200814 return
815 try:
816 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
817 msg = msg % (getdoc(name_or_class).strip(),
818 real_value, str(name_or_class(real_value)),
819 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100820 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200821 msg = "Invalid %s: %s does not match expected value %s" % (
822 name_or_class, real_value, expected_value)
823
824 self.assertEqual(real_value, expected_value, msg)
825
Klement Sekerab17dd962017-01-09 07:43:48 +0100826 def assert_in_range(self,
827 real_value,
828 expected_min,
829 expected_max,
830 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200831 if name is None:
832 msg = None
833 else:
834 msg = "Invalid %s: %s out of range <%s,%s>" % (
835 name, real_value, expected_min, expected_max)
836 self.assertTrue(expected_min <= real_value <= expected_max, msg)
837
Klement Sekerad81ae412018-05-16 10:52:54 +0200838 def assert_packet_checksums_valid(self, packet,
839 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200840 received = packet.__class__(str(packet))
841 self.logger.debug(
842 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200843 udp_layers = ['UDP', 'UDPerror']
844 checksum_fields = ['cksum', 'chksum']
845 checksums = []
846 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200847 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200848 while True:
849 layer = temp.getlayer(counter)
850 if layer:
851 for cf in checksum_fields:
852 if hasattr(layer, cf):
853 if ignore_zero_udp_checksums and \
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800854 0 == getattr(layer, cf) and \
855 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200856 continue
857 delattr(layer, cf)
858 checksums.append((counter, cf))
859 else:
860 break
861 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200862 if 0 == len(checksums):
863 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200864 temp = temp.__class__(str(temp))
865 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200866 calc_sum = getattr(temp[layer], cf)
867 self.assert_equal(
868 getattr(received[layer], cf), calc_sum,
869 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
870 self.logger.debug(
871 "Checksum field `%s` on `%s` layer has correct value `%s`" %
872 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200873
874 def assert_checksum_valid(self, received_packet, layer,
875 field_name='chksum',
876 ignore_zero_checksum=False):
877 """ Check checksum of received packet on given layer """
878 received_packet_checksum = getattr(received_packet[layer], field_name)
879 if ignore_zero_checksum and 0 == received_packet_checksum:
880 return
881 recalculated = received_packet.__class__(str(received_packet))
882 delattr(recalculated[layer], field_name)
883 recalculated = recalculated.__class__(str(recalculated))
884 self.assert_equal(received_packet_checksum,
885 getattr(recalculated[layer], field_name),
886 "packet checksum on layer: %s" % layer)
887
888 def assert_ip_checksum_valid(self, received_packet,
889 ignore_zero_checksum=False):
890 self.assert_checksum_valid(received_packet, 'IP',
891 ignore_zero_checksum=ignore_zero_checksum)
892
893 def assert_tcp_checksum_valid(self, received_packet,
894 ignore_zero_checksum=False):
895 self.assert_checksum_valid(received_packet, 'TCP',
896 ignore_zero_checksum=ignore_zero_checksum)
897
898 def assert_udp_checksum_valid(self, received_packet,
899 ignore_zero_checksum=True):
900 self.assert_checksum_valid(received_packet, 'UDP',
901 ignore_zero_checksum=ignore_zero_checksum)
902
903 def assert_embedded_icmp_checksum_valid(self, received_packet):
904 if received_packet.haslayer(IPerror):
905 self.assert_checksum_valid(received_packet, 'IPerror')
906 if received_packet.haslayer(TCPerror):
907 self.assert_checksum_valid(received_packet, 'TCPerror')
908 if received_packet.haslayer(UDPerror):
909 self.assert_checksum_valid(received_packet, 'UDPerror',
910 ignore_zero_checksum=True)
911 if received_packet.haslayer(ICMPerror):
912 self.assert_checksum_valid(received_packet, 'ICMPerror')
913
914 def assert_icmp_checksum_valid(self, received_packet):
915 self.assert_checksum_valid(received_packet, 'ICMP')
916 self.assert_embedded_icmp_checksum_valid(received_packet)
917
918 def assert_icmpv6_checksum_valid(self, pkt):
919 if pkt.haslayer(ICMPv6DestUnreach):
920 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
921 self.assert_embedded_icmp_checksum_valid(pkt)
922 if pkt.haslayer(ICMPv6EchoRequest):
923 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
924 if pkt.haslayer(ICMPv6EchoReply):
925 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
926
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100927 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +0100928 if counter.startswith("/"):
929 counter_value = self.statistics.get_counter(counter)
930 self.assert_equal(counter_value, expected_value,
931 "packet counter `%s'" % counter)
932 else:
933 counters = self.vapi.cli("sh errors").split('\n')
934 counter_value = -1
935 for i in range(1, len(counters) - 1):
936 results = counters[i].split()
937 if results[1] == counter:
938 counter_value = int(results[0])
939 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100940
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100941 @classmethod
942 def sleep(cls, timeout, remark=None):
943 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800944 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000945 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100946 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000947 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800948 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200949 cls.logger.error("unexpected time.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800950 "slept for %es instead of ~%es!",
951 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000952 if hasattr(cls, 'logger'):
953 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800954 "Finished sleep (%s) - slept %es (wanted %es)",
955 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +0100956
Neale Ranns947ea622018-06-07 23:48:20 -0700957 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800958 self.vapi.cli("clear trace")
959 intf.add_stream(pkts)
960 self.pg_enable_capture(self.pg_interfaces)
961 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700962 if not timeout:
963 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800964 for i in self.pg_interfaces:
965 i.get_capture(0, timeout=timeout)
966 i.assert_nothing_captured(remark=remark)
967 timeout = 0.1
968
969 def send_and_expect(self, input, pkts, output):
970 self.vapi.cli("clear trace")
971 input.add_stream(pkts)
972 self.pg_enable_capture(self.pg_interfaces)
973 self.pg_start()
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700974 if isinstance(object, (list,)):
975 rx = []
976 for o in output:
977 rx.append(output.get_capture(len(pkts)))
978 else:
979 rx = output.get_capture(len(pkts))
980 return rx
981
982 def send_and_expect_only(self, input, pkts, output, timeout=None):
983 self.vapi.cli("clear trace")
984 input.add_stream(pkts)
985 self.pg_enable_capture(self.pg_interfaces)
986 self.pg_start()
987 if isinstance(object, (list,)):
988 outputs = output
989 rx = []
990 for o in outputs:
991 rx.append(output.get_capture(len(pkts)))
992 else:
993 rx = output.get_capture(len(pkts))
994 outputs = [output]
995 if not timeout:
996 timeout = 1
997 for i in self.pg_interfaces:
998 if i not in outputs:
999 i.get_capture(0, timeout=timeout)
1000 i.assert_nothing_captured()
1001 timeout = 0.1
1002
Neale Ranns52fae862018-01-08 04:41:42 -08001003 return rx
1004
Damjan Marionf56b77a2016-10-03 19:44:57 +02001005
juraj.linkes184870a2018-07-16 14:22:01 +02001006def get_testcase_doc_name(test):
1007 return getdoc(test.__class__).splitlines()[0]
1008
1009
Ole Trøan5ba91592018-11-22 10:01:09 +00001010def get_test_description(descriptions, test):
1011 short_description = test.shortDescription()
1012 if descriptions and short_description:
1013 return short_description
1014 else:
1015 return str(test)
1016
1017
juraj.linkes40dd73b2018-09-21 13:55:16 +02001018class TestCaseInfo(object):
1019 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1020 self.logger = logger
1021 self.tempdir = tempdir
1022 self.vpp_pid = vpp_pid
1023 self.vpp_bin_path = vpp_bin_path
1024 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001025
1026
Damjan Marionf56b77a2016-10-03 19:44:57 +02001027class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001028 """
1029 @property result_string
1030 String variable to store the test case result string.
1031 @property errors
1032 List variable containing 2-tuples of TestCase instances and strings
1033 holding formatted tracebacks. Each tuple represents a test which
1034 raised an unexpected exception.
1035 @property failures
1036 List variable containing 2-tuples of TestCase instances and strings
1037 holding formatted tracebacks. Each tuple represents a test where
1038 a failure was explicitly signalled using the TestCase.assert*()
1039 methods.
1040 """
1041
juraj.linkes40dd73b2018-09-21 13:55:16 +02001042 failed_test_cases_info = set()
1043 core_crash_test_cases_info = set()
1044 current_test_case_info = None
1045
juraj.linkesabec0122018-11-16 17:28:56 +01001046 def __init__(self, stream, descriptions, verbosity, runner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001047 """
Klement Sekerada505f62017-01-04 12:58:53 +01001048 :param stream File descriptor to store where to report test results.
1049 Set to the standard error stream by default.
1050 :param descriptions Boolean variable to store information if to use
1051 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001052 :param verbosity Integer variable to store required verbosity level.
1053 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001054 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
1055 self.stream = stream
1056 self.descriptions = descriptions
1057 self.verbosity = verbosity
1058 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001059 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001060
Damjan Marionf56b77a2016-10-03 19:44:57 +02001061 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001062 """
1063 Record a test succeeded result
1064
1065 :param test:
1066
1067 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001068 if self.current_test_case_info:
1069 self.current_test_case_info.logger.debug(
1070 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1071 test._testMethodName,
1072 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001073 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001074 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001075
juraj.linkescae64f82018-09-19 15:01:47 +02001076 self.send_result_through_pipe(test, PASS)
1077
Klement Sekeraf62ae122016-10-11 11:47:09 +02001078 def addSkip(self, test, reason):
1079 """
1080 Record a test skipped.
1081
1082 :param test:
1083 :param reason:
1084
1085 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001086 if self.current_test_case_info:
1087 self.current_test_case_info.logger.debug(
1088 "--- addSkip() %s.%s(%s) called, reason is %s" %
1089 (test.__class__.__name__, test._testMethodName,
1090 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001091 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001092 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001093
juraj.linkescae64f82018-09-19 15:01:47 +02001094 self.send_result_through_pipe(test, SKIP)
1095
juraj.linkes40dd73b2018-09-21 13:55:16 +02001096 def symlink_failed(self):
1097 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001098 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001099 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001100 link_path = os.path.join(
1101 failed_dir,
1102 '%s-FAILED' %
1103 os.path.basename(self.current_test_case_info.tempdir))
1104 if self.current_test_case_info.logger:
1105 self.current_test_case_info.logger.debug(
1106 "creating a link to the failed test")
1107 self.current_test_case_info.logger.debug(
1108 "os.symlink(%s, %s)" %
1109 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001110 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001111 if self.current_test_case_info.logger:
1112 self.current_test_case_info.logger.debug(
1113 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001114 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001115 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001116
Klement Sekeraf413bef2017-08-15 07:09:02 +02001117 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001118 if self.current_test_case_info.logger:
1119 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001120
juraj.linkescae64f82018-09-19 15:01:47 +02001121 def send_result_through_pipe(self, test, result):
1122 if hasattr(self, 'test_framework_result_pipe'):
1123 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001124 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001125 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001126
juraj.linkes40dd73b2018-09-21 13:55:16 +02001127 def log_error(self, test, err, fn_name):
1128 if self.current_test_case_info:
1129 if isinstance(test, unittest.suite._ErrorHolder):
1130 test_name = test.description
1131 else:
1132 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1133 test._testMethodName,
1134 test._testMethodDoc)
1135 self.current_test_case_info.logger.debug(
1136 "--- %s() %s called, err is %s" %
1137 (fn_name, test_name, err))
1138 self.current_test_case_info.logger.debug(
1139 "formatted exception is:\n%s" %
1140 "".join(format_exception(*err)))
1141
1142 def add_error(self, test, err, unittest_fn, error_type):
1143 if error_type == FAIL:
1144 self.log_error(test, err, 'addFailure')
1145 error_type_str = colorize("FAIL", RED)
1146 elif error_type == ERROR:
1147 self.log_error(test, err, 'addError')
1148 error_type_str = colorize("ERROR", RED)
1149 else:
1150 raise Exception('Error type %s cannot be used to record an '
1151 'error or a failure' % error_type)
1152
1153 unittest_fn(self, test, err)
1154 if self.current_test_case_info:
1155 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1156 (error_type_str,
1157 self.current_test_case_info.tempdir)
1158 self.symlink_failed()
1159 self.failed_test_cases_info.add(self.current_test_case_info)
1160 if is_core_present(self.current_test_case_info.tempdir):
1161 if not self.current_test_case_info.core_crash_test:
1162 if isinstance(test, unittest.suite._ErrorHolder):
1163 test_name = str(test)
1164 else:
1165 test_name = "'{}' ({})".format(
1166 get_testcase_doc_name(test), test.id())
1167 self.current_test_case_info.core_crash_test = test_name
1168 self.core_crash_test_cases_info.add(
1169 self.current_test_case_info)
1170 else:
1171 self.result_string = '%s [no temp dir]' % error_type_str
1172
1173 self.send_result_through_pipe(test, error_type)
1174
Damjan Marionf56b77a2016-10-03 19:44:57 +02001175 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001176 """
1177 Record a test failed result
1178
1179 :param test:
1180 :param err: error message
1181
1182 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001183 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001184
Damjan Marionf56b77a2016-10-03 19:44:57 +02001185 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001186 """
1187 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001188
Klement Sekeraf62ae122016-10-11 11:47:09 +02001189 :param test:
1190 :param err: error message
1191
1192 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001193 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001194
Damjan Marionf56b77a2016-10-03 19:44:57 +02001195 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001196 """
1197 Get test description
1198
1199 :param test:
1200 :returns: test description
1201
1202 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001203 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001204
Damjan Marionf56b77a2016-10-03 19:44:57 +02001205 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001206 """
1207 Start a test
1208
1209 :param test:
1210
1211 """
Paul Vinciguerra86ebba62018-11-21 09:28:32 -08001212 test.print_header()
juraj.linkes40dd73b2018-09-21 13:55:16 +02001213
Damjan Marionf56b77a2016-10-03 19:44:57 +02001214 unittest.TestResult.startTest(self, test)
1215 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001216 self.stream.writeln(
1217 "Starting " + self.getDescription(test) + " ...")
1218 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001219
Damjan Marionf56b77a2016-10-03 19:44:57 +02001220 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001221 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001222 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001223
1224 :param test:
1225
1226 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001227 unittest.TestResult.stopTest(self, test)
1228 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001229 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001230 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001231 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001232 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001233 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001234 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001235 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001236
1237 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001238
Damjan Marionf56b77a2016-10-03 19:44:57 +02001239 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001240 """
1241 Print errors from running the test case
1242 """
juraj.linkesabec0122018-11-16 17:28:56 +01001243 if len(self.errors) > 0 or len(self.failures) > 0:
1244 self.stream.writeln()
1245 self.printErrorList('ERROR', self.errors)
1246 self.printErrorList('FAIL', self.failures)
1247
1248 # ^^ that is the last output from unittest before summary
1249 if not self.runner.print_summary:
1250 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1251 self.stream = devnull
1252 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001253
Damjan Marionf56b77a2016-10-03 19:44:57 +02001254 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001255 """
1256 Print error list to the output stream together with error type
1257 and test case description.
1258
1259 :param flavour: error type
1260 :param errors: iterable errors
1261
1262 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001263 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001264 self.stream.writeln(double_line_delim)
1265 self.stream.writeln("%s: %s" %
1266 (flavour, self.getDescription(test)))
1267 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001268 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001269
1270
Damjan Marionf56b77a2016-10-03 19:44:57 +02001271class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001272 """
Klement Sekera104543f2017-02-03 07:29:43 +01001273 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001274 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001275
Klement Sekeraf62ae122016-10-11 11:47:09 +02001276 @property
1277 def resultclass(self):
1278 """Class maintaining the results of the tests"""
1279 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001280
juraj.linkes184870a2018-07-16 14:22:01 +02001281 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001282 result_pipe=None, failfast=False, buffer=False,
juraj.linkesabec0122018-11-16 17:28:56 +01001283 resultclass=None, print_summary=True):
Klement Sekera7a161da2017-01-17 13:42:48 +01001284 # ignore stream setting here, use hard-coded stdout to be in sync
1285 # with prints from VppTestCase methods ...
1286 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1287 verbosity, failfast, buffer,
1288 resultclass)
juraj.linkesccfead62018-11-21 13:20:43 +01001289 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001290
juraj.linkesabec0122018-11-16 17:28:56 +01001291 self.orig_stream = self.stream
1292 self.resultclass.test_framework_result_pipe = result_pipe
1293
1294 self.print_summary = print_summary
1295
1296 def _makeResult(self):
1297 return self.resultclass(self.stream,
1298 self.descriptions,
1299 self.verbosity,
1300 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001301
Damjan Marionf56b77a2016-10-03 19:44:57 +02001302 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001303 """
1304 Run the tests
1305
1306 :param test:
1307
1308 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001309 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001310
1311 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001312 if not self.print_summary:
1313 self.stream = self.orig_stream
1314 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001315 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001316
1317
1318class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001319 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001320 self.logger = logger
1321 self.args = args
1322 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001323 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001324 super(Worker, self).__init__()
1325
1326 def run(self):
1327 executable = self.args[0]
1328 self.logger.debug("Running executable w/args `%s'" % self.args)
1329 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001330 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001331 env["CK_LOG_FILE_NAME"] = "-"
1332 self.process = subprocess.Popen(
1333 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1334 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1335 out, err = self.process.communicate()
1336 self.logger.debug("Finished running `%s'" % executable)
1337 self.logger.info("Return code is `%s'" % self.process.returncode)
1338 self.logger.info(single_line_delim)
1339 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1340 self.logger.info(single_line_delim)
1341 self.logger.info(out)
1342 self.logger.info(single_line_delim)
1343 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1344 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001345 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001346 self.logger.info(single_line_delim)
1347 self.result = self.process.returncode