blob: 7633ca04d0bd3ecbafd6c7f99ac1d2bd30ac1493 [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
Paul Vinciguerra919efad2018-12-17 21:43:43 -080023from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010024from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000025from 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
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080043# Python2/3 compatible
44try:
45 input = raw_input
46except NameError:
47 pass
48
juraj.linkescae64f82018-09-19 15:01:47 +020049PASS = 0
50FAIL = 1
51ERROR = 2
52SKIP = 3
53TEST_RUN = 4
54
Klement Sekeraebbaf552018-02-17 13:41:33 +010055debug_framework = False
56if os.getenv('TEST_DEBUG', "0") == "1":
57 debug_framework = True
58 import debug_internal
59
Klement Sekeraf62ae122016-10-11 11:47:09 +020060"""
61 Test framework module.
62
63 The module provides a set of tools for constructing and running tests and
64 representing the results.
65"""
66
Klement Sekeraf62ae122016-10-11 11:47:09 +020067
Damjan Marionf56b77a2016-10-03 19:44:57 +020068class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020069 """Private class to create packet info object.
70
71 Help process information about the next packet.
72 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020073 """
Matej Klotton86d87c42016-11-11 11:38:55 +010074 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020075 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010076 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020077 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010078 #: Store the index of the destination packet generator interface
79 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020080 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010081 #: Store expected ip version
82 ip = -1
83 #: Store expected upper protocol
84 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010085 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020086 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020087
Matej Klotton16a14cd2016-12-07 15:09:13 +010088 def __eq__(self, other):
89 index = self.index == other.index
90 src = self.src == other.src
91 dst = self.dst == other.dst
92 data = self.data == other.data
93 return index and src and dst and data
94
Klement Sekeraf62ae122016-10-11 11:47:09 +020095
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010096def pump_output(testclass):
97 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +010098 stdout_fragment = ""
99 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400100 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100101 readable = select.select([testclass.vpp.stdout.fileno(),
102 testclass.vpp.stderr.fileno(),
103 testclass.pump_thread_wakeup_pipe[0]],
104 [], [])[0]
105 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100106 read = os.read(testclass.vpp.stdout.fileno(), 102400)
107 if len(read) > 0:
108 split = read.splitlines(True)
109 if len(stdout_fragment) > 0:
110 split[0] = "%s%s" % (stdout_fragment, split[0])
111 if len(split) > 0 and split[-1].endswith("\n"):
112 limit = None
113 else:
114 limit = -1
115 stdout_fragment = split[-1]
116 testclass.vpp_stdout_deque.extend(split[:limit])
117 if not testclass.cache_vpp_output:
118 for line in split[:limit]:
119 testclass.logger.debug(
120 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100121 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100122 read = os.read(testclass.vpp.stderr.fileno(), 102400)
123 if len(read) > 0:
124 split = read.splitlines(True)
125 if len(stderr_fragment) > 0:
126 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan7f991832018-12-06 17:35:12 +0100127 if len(split) > 0 and split[-1].endswith(b"\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100128 limit = None
129 else:
130 limit = -1
131 stderr_fragment = split[-1]
132 testclass.vpp_stderr_deque.extend(split[:limit])
133 if not testclass.cache_vpp_output:
134 for line in split[:limit]:
135 testclass.logger.debug(
136 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800137 # ignoring the dummy pipe here intentionally - the
138 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200139
140
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800141def _is_skip_aarch64_set():
juraj.linkes68ebc832018-11-29 09:37:08 +0100142 return os.getenv('SKIP_AARCH64', 'n').lower() in ('yes', 'y', '1')
143
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800144is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100145
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800146
147def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100148 return platform.machine() == 'aarch64'
149
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800150is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100151
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800152
153def _running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100154 s = os.getenv("EXTENDED_TESTS", "n")
155 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100156
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800157running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100158
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800159
160def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100161 os_id = os.getenv("OS_ID", "")
162 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200163
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800164running_on_centos = _running_on_centos
165
Klement Sekerad3e671e2017-09-29 12:36:37 +0200166
Klement Sekera909a6a12017-08-08 04:33:53 +0200167class KeepAliveReporter(object):
168 """
169 Singleton object which reports test start to parent process
170 """
171 _shared_state = {}
172
173 def __init__(self):
174 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800175 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200176
177 @property
178 def pipe(self):
179 return self._pipe
180
181 @pipe.setter
182 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800183 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200184 raise Exception("Internal error - pipe should only be set once.")
185 self._pipe = pipe
186
juraj.linkes40dd73b2018-09-21 13:55:16 +0200187 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200188 """
189 Write current test tmpdir & desc to keep-alive pipe to signal liveness
190 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200191 if self.pipe is None:
192 # if not running forked..
193 return
194
Klement Sekera909a6a12017-08-08 04:33:53 +0200195 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200196 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200197 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200198 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200199
Dave Wallacee2efd122017-09-30 22:04:21 -0400200 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200201
202
Damjan Marionf56b77a2016-10-03 19:44:57 +0200203class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100204 """This subclass is a base class for VPP test cases that are implemented as
205 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200206 """
207
Ole Troana45dc072018-12-21 16:04:22 +0100208 extra_vpp_punt_config = []
209 extra_vpp_plugin_config = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100210
Klement Sekeraf62ae122016-10-11 11:47:09 +0200211 @property
212 def packet_infos(self):
213 """List of packet infos"""
214 return self._packet_infos
215
Klement Sekeradab231a2016-12-21 08:50:14 +0100216 @classmethod
217 def get_packet_count_for_if_idx(cls, dst_if_index):
218 """Get the number of packet info for specified destination if index"""
219 if dst_if_index in cls._packet_count_for_dst_if_idx:
220 return cls._packet_count_for_dst_if_idx[dst_if_index]
221 else:
222 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200223
224 @classmethod
225 def instance(cls):
226 """Return the instance of this testcase"""
227 return cls.test_instance
228
Damjan Marionf56b77a2016-10-03 19:44:57 +0200229 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200230 def set_debug_flags(cls, d):
231 cls.debug_core = False
232 cls.debug_gdb = False
233 cls.debug_gdbserver = False
234 if d is None:
235 return
236 dl = d.lower()
237 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200238 cls.debug_core = True
239 elif dl == "gdb":
240 cls.debug_gdb = True
241 elif dl == "gdbserver":
242 cls.debug_gdbserver = True
243 else:
244 raise Exception("Unrecognized DEBUG option: '%s'" % d)
245
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800246 @staticmethod
247 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200248 cpu_usage_list = [set(range(psutil.cpu_count()))]
249 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
250 if 'vpp_main' == p.info['name']]
251 for vpp_process in vpp_processes:
252 for cpu_usage_set in cpu_usage_list:
253 try:
254 cpu_num = vpp_process.cpu_num()
255 if cpu_num in cpu_usage_set:
256 cpu_usage_set_index = cpu_usage_list.index(
257 cpu_usage_set)
258 if cpu_usage_set_index == len(cpu_usage_list) - 1:
259 cpu_usage_list.append({cpu_num})
260 else:
261 cpu_usage_list[cpu_usage_set_index + 1].add(
262 cpu_num)
263 cpu_usage_set.remove(cpu_num)
264 break
265 except psutil.NoSuchProcess:
266 pass
267
268 for cpu_usage_set in cpu_usage_list:
269 if len(cpu_usage_set) > 0:
270 min_usage_set = cpu_usage_set
271 break
272
273 return random.choice(tuple(min_usage_set))
274
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800275 @classmethod
juraj.linkes40dd73b2018-09-21 13:55:16 +0200276 def print_header(cls):
277 if not hasattr(cls, '_header_printed'):
278 print(double_line_delim)
279 print(colorize(getdoc(cls).splitlines()[0], GREEN))
280 print(double_line_delim)
281 cls._header_printed = True
282
juraj.linkes184870a2018-07-16 14:22:01 +0200283 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200284 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200285 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100286 s = os.getenv("STEP", "n")
287 cls.step = True if s.lower() in ("y", "yes", "1") else False
288 d = os.getenv("DEBUG", None)
289 c = os.getenv("CACHE_OUTPUT", "1")
290 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200291 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100292 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
293 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100294 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
295 plugin_path = None
296 if cls.plugin_path is not None:
297 if cls.extern_plugin_path is not None:
298 plugin_path = "%s:%s" % (
299 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100300 else:
301 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100302 elif cls.extern_plugin_path is not None:
303 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100304 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100305 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100306 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100307 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100308 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100309 if size is not None:
310 coredump_size = "coredump-size %s" % size
311 if coredump_size is None:
312 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200313
Ole Troana45dc072018-12-21 16:04:22 +0100314 cpu_core_number = cls.get_least_used_cpu()
juraj.linkes184870a2018-07-16 14:22:01 +0200315
Ole Troana45dc072018-12-21 16:04:22 +0100316 cls.vpp_cmdline = [cls.vpp_bin, "unix",
317 "{", "nodaemon", debug_cli, "full-coredump",
318 coredump_size, "runtime-dir", cls.tempdir, "}",
319 "api-trace", "{", "on", "}", "api-segment", "{",
320 "prefix", cls.shm_prefix, "}", "cpu", "{",
321 "main-core", str(cpu_core_number), "}", "statseg",
322 "{", "socket-name", cls.stats_sock, "}", "plugins",
323 "{", "plugin", "dpdk_plugin.so", "{", "disable",
324 "}", "plugin", "unittest_plugin.so", "{", "enable",
325 "}"] + cls.extra_vpp_plugin_config + ["}", ]
326 if cls.extra_vpp_punt_config is not None:
327 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100328 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100329 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100330 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
331 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200332
333 @classmethod
334 def wait_for_enter(cls):
335 if cls.debug_gdbserver:
336 print(double_line_delim)
337 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
338 elif cls.debug_gdb:
339 print(double_line_delim)
340 print("Spawned VPP with PID: %d" % cls.vpp.pid)
341 else:
342 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
343 return
344 print(single_line_delim)
345 print("You can debug the VPP using e.g.:")
346 if cls.debug_gdbserver:
347 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
348 print("Now is the time to attach a gdb by running the above "
349 "command, set up breakpoints etc. and then resume VPP from "
350 "within gdb by issuing the 'continue' command")
351 elif cls.debug_gdb:
352 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
353 print("Now is the time to attach a gdb by running the above "
354 "command and set up breakpoints etc.")
355 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800356 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200357
358 @classmethod
359 def run_vpp(cls):
360 cmdline = cls.vpp_cmdline
361
362 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100363 gdbserver = '/usr/bin/gdbserver'
364 if not os.path.isfile(gdbserver) or \
365 not os.access(gdbserver, os.X_OK):
366 raise Exception("gdbserver binary '%s' does not exist or is "
367 "not executable" % gdbserver)
368
369 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200370 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
371
Klement Sekera931be3a2016-11-03 05:36:01 +0100372 try:
373 cls.vpp = subprocess.Popen(cmdline,
374 stdout=subprocess.PIPE,
375 stderr=subprocess.PIPE,
376 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800377 except subprocess.CalledProcessError as e:
Klement Sekera931be3a2016-11-03 05:36:01 +0100378 cls.logger.critical("Couldn't start vpp: %s" % e)
379 raise
380
Klement Sekera277b89c2016-10-28 13:20:27 +0200381 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100382
Damjan Marionf56b77a2016-10-03 19:44:57 +0200383 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200384 def wait_for_stats_socket(cls):
385 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800386 ok = False
387 while time.time() < deadline or \
388 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200389 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800390 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200391 break
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800392 time.sleep(0.8)
393 if not ok:
394 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200395
396 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200397 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200398 """
399 Perform class setup before running the testcase
400 Remove shared memory files, start vpp and connect the vpp-api
401 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100402 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100403 random.seed()
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800404 cls.print_header()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100405 cls.logger = get_logger(cls.__name__)
406 if hasattr(cls, 'parallel_handler'):
407 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100408 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200409 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200410 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200411 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200412 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
413 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100414 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
415 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200416 cls.file_handler.setLevel(DEBUG)
417 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200418 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200419 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200420 cls.logger.info("Temporary dir is %s, shm prefix is %s",
421 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200422 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100423 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100424 cls._captures = []
425 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200426 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100427 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100428 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200429 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200430 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200431 # need to catch exceptions here because if we raise, then the cleanup
432 # doesn't get called and we might end with a zombie vpp
433 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200434 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200435 cls.reporter.send_keep_alive(cls, 'setUpClass')
436 VppTestResult.current_test_case_info = TestCaseInfo(
437 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100438 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100439 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100440 cls.pump_thread_stop_flag = Event()
441 cls.pump_thread_wakeup_pipe = os.pipe()
442 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100443 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100444 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200445 if cls.debug_gdb or cls.debug_gdbserver:
446 read_timeout = 0
447 else:
448 read_timeout = 5
449 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
450 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100451 if cls.step:
452 hook = StepHook(cls)
453 else:
454 hook = PollHook(cls)
455 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200456 cls.wait_for_stats_socket()
457 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200458 try:
459 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100460 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200461 cls.vpp_startup_failed = True
462 cls.logger.critical(
463 "VPP died shortly after startup, check the"
464 " output to standard error for possible cause")
465 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100466 try:
467 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100468 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100469 try:
470 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100471 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100472 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100473 if cls.debug_gdbserver:
474 print(colorize("You're running VPP inside gdbserver but "
475 "VPP-API connection failed, did you forget "
476 "to 'continue' VPP from within gdb?", RED))
477 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100478 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100479 try:
480 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100481 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100482 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100483 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200484
Damjan Marionf56b77a2016-10-03 19:44:57 +0200485 @classmethod
486 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200487 """
488 Disconnect vpp-api, kill vpp and cleanup shared memory files
489 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200490 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
491 cls.vpp.poll()
492 if cls.vpp.returncode is None:
493 print(double_line_delim)
494 print("VPP or GDB server is still running")
495 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800496 input("When done debugging, press ENTER to kill the "
497 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200498
juraj.linkes184870a2018-07-16 14:22:01 +0200499 # first signal that we want to stop the pump thread, then wake it up
500 if hasattr(cls, 'pump_thread_stop_flag'):
501 cls.pump_thread_stop_flag.set()
502 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100503 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100504 if hasattr(cls, 'pump_thread'):
505 cls.logger.debug("Waiting for pump thread to stop")
506 cls.pump_thread.join()
507 if hasattr(cls, 'vpp_stderr_reader_thread'):
508 cls.logger.debug("Waiting for stdderr pump to stop")
509 cls.vpp_stderr_reader_thread.join()
510
Klement Sekeraf62ae122016-10-11 11:47:09 +0200511 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100512 if hasattr(cls, 'vapi'):
513 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100514 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200515 cls.vpp.poll()
516 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100517 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200518 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100519 cls.logger.debug("Waiting for vpp to die")
520 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200521 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200522
Klement Sekera3747c752017-04-10 06:30:17 +0200523 if cls.vpp_startup_failed:
524 stdout_log = cls.logger.info
525 stderr_log = cls.logger.critical
526 else:
527 stdout_log = cls.logger.info
528 stderr_log = cls.logger.info
529
Klement Sekerae4504c62016-12-08 10:16:41 +0100530 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200531 stdout_log(single_line_delim)
532 stdout_log('VPP output to stdout while running %s:', cls.__name__)
533 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100534 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200535 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
536 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200537 stdout_log('\n%s', vpp_output)
538 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200539
Klement Sekerae4504c62016-12-08 10:16:41 +0100540 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200541 stderr_log(single_line_delim)
542 stderr_log('VPP output to stderr while running %s:', cls.__name__)
543 stderr_log(single_line_delim)
Ole Troan7f991832018-12-06 17:35:12 +0100544 vpp_output = "".join(str(cls.vpp_stderr_deque))
Klement Sekera027dbd52017-04-11 06:01:53 +0200545 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
546 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200547 stderr_log('\n%s', vpp_output)
548 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200549
Damjan Marionf56b77a2016-10-03 19:44:57 +0200550 @classmethod
551 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200552 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200553 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200554 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200555 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100556 cls.reset_packet_infos()
557 if debug_framework:
558 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200559
Damjan Marionf56b77a2016-10-03 19:44:57 +0200560 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200561 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100562 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
563 (self.__class__.__name__, self._testMethodName,
564 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200565 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200566 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700567 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200568 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200569 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200570 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800571 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100572 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500573 # Save/Dump VPP api trace log
574 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
575 tmp_api_trace = "/tmp/%s" % api_trace
576 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
577 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
578 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
579 vpp_api_trace_log))
580 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500581 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500582 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100583 else:
584 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200585
Damjan Marionf56b77a2016-10-03 19:44:57 +0200586 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200587 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200588 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100589 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
590 (self.__class__.__name__, self._testMethodName,
591 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100592 if self.vpp_dead:
593 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100594 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100595 self.vpp_stdout_deque.append(
596 "--- test setUp() for %s.%s(%s) starts here ---\n" %
597 (self.__class__.__name__, self._testMethodName,
598 self._testMethodDoc))
599 self.vpp_stderr_deque.append(
600 "--- test setUp() for %s.%s(%s) starts here ---\n" %
601 (self.__class__.__name__, self._testMethodName,
602 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200603 self.vapi.cli("clear trace")
604 # store the test instance inside the test class - so that objects
605 # holding the class can access instance methods (like assertEqual)
606 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200607
Damjan Marionf56b77a2016-10-03 19:44:57 +0200608 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200609 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200610 """
611 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200612
Klement Sekera75e7d132017-09-20 08:26:30 +0200613 :param interfaces: iterable interface indexes (if None,
614 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200615
Klement Sekeraf62ae122016-10-11 11:47:09 +0200616 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200617 if interfaces is None:
618 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200619 for i in interfaces:
620 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200621
Damjan Marionf56b77a2016-10-03 19:44:57 +0200622 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100623 def register_capture(cls, cap_name):
624 """ Register a capture in the testclass """
625 # add to the list of captures with current timestamp
626 cls._captures.append((time.time(), cap_name))
627 # filter out from zombies
628 cls._zombie_captures = [(stamp, name)
629 for (stamp, name) in cls._zombie_captures
630 if name != cap_name]
631
632 @classmethod
633 def pg_start(cls):
634 """ Remove any zombie captures and enable the packet generator """
635 # how long before capture is allowed to be deleted - otherwise vpp
636 # crashes - 100ms seems enough (this shouldn't be needed at all)
637 capture_ttl = 0.1
638 now = time.time()
639 for stamp, cap_name in cls._zombie_captures:
640 wait = stamp + capture_ttl - now
641 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100642 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100643 now = time.time()
644 cls.logger.debug("Removing zombie capture %s" % cap_name)
645 cls.vapi.cli('packet-generator delete %s' % cap_name)
646
Klement Sekeraf62ae122016-10-11 11:47:09 +0200647 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
648 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100649 cls._zombie_captures = cls._captures
650 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200651
Damjan Marionf56b77a2016-10-03 19:44:57 +0200652 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200653 def create_pg_interfaces(cls, interfaces):
654 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100655 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200656
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100657 :param interfaces: iterable indexes of the interfaces.
658 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200659
Klement Sekeraf62ae122016-10-11 11:47:09 +0200660 """
661 result = []
662 for i in interfaces:
663 intf = VppPGInterface(cls, i)
664 setattr(cls, intf.name, intf)
665 result.append(intf)
666 cls.pg_interfaces = result
667 return result
668
Matej Klotton0178d522016-11-04 11:11:44 +0100669 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200670 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100671 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100672 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100673
Klement Sekerab9ef2732018-06-24 22:49:33 +0200674 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100675 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100676 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200677 result = [VppLoInterface(cls) for i in range(count)]
678 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100679 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100680 cls.lo_interfaces = result
681 return result
682
Damjan Marionf56b77a2016-10-03 19:44:57 +0200683 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200684 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200685 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200686 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200687 NOTE: Currently works only when Raw layer is present.
688
689 :param packet: packet
690 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200691 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200692
693 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200694 packet_len = len(packet) + 4
695 extend = size - packet_len
696 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200697 num = (extend / len(padding)) + 1
698 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200699
Klement Sekeradab231a2016-12-21 08:50:14 +0100700 @classmethod
701 def reset_packet_infos(cls):
702 """ Reset the list of packet info objects and packet counts to zero """
703 cls._packet_infos = {}
704 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200705
Klement Sekeradab231a2016-12-21 08:50:14 +0100706 @classmethod
707 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200708 """
709 Create packet info object containing the source and destination indexes
710 and add it to the testcase's packet info list
711
Klement Sekeradab231a2016-12-21 08:50:14 +0100712 :param VppInterface src_if: source interface
713 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200714
715 :returns: _PacketInfo object
716
717 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200718 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100719 info.index = len(cls._packet_infos)
720 info.src = src_if.sw_if_index
721 info.dst = dst_if.sw_if_index
722 if isinstance(dst_if, VppSubInterface):
723 dst_idx = dst_if.parent.sw_if_index
724 else:
725 dst_idx = dst_if.sw_if_index
726 if dst_idx in cls._packet_count_for_dst_if_idx:
727 cls._packet_count_for_dst_if_idx[dst_idx] += 1
728 else:
729 cls._packet_count_for_dst_if_idx[dst_idx] = 1
730 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200731 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200732
Damjan Marionf56b77a2016-10-03 19:44:57 +0200733 @staticmethod
734 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200735 """
736 Convert _PacketInfo object to packet payload
737
738 :param info: _PacketInfo object
739
740 :returns: string containing serialized data from packet info
741 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100742 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
743 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200744
Damjan Marionf56b77a2016-10-03 19:44:57 +0200745 @staticmethod
746 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200747 """
748 Convert packet payload to _PacketInfo object
749
750 :param payload: packet payload
751
752 :returns: _PacketInfo object containing de-serialized data from payload
753
754 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200755 numbers = payload.split()
756 info = _PacketInfo()
757 info.index = int(numbers[0])
758 info.src = int(numbers[1])
759 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100760 info.ip = int(numbers[3])
761 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200762 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200763
Damjan Marionf56b77a2016-10-03 19:44:57 +0200764 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200765 """
766 Iterate over the packet info list stored in the testcase
767 Start iteration with first element if info is None
768 Continue based on index in info if info is specified
769
770 :param info: info or None
771 :returns: next info in list or None if no more infos
772 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200773 if info is None:
774 next_index = 0
775 else:
776 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100777 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200778 return None
779 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100780 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200781
Klement Sekeraf62ae122016-10-11 11:47:09 +0200782 def get_next_packet_info_for_interface(self, src_index, info):
783 """
784 Search the packet info list for the next packet info with same source
785 interface index
786
787 :param src_index: source interface index to search for
788 :param info: packet info - where to start the search
789 :returns: packet info or None
790
791 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200792 while True:
793 info = self.get_next_packet_info(info)
794 if info is None:
795 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200796 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200797 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200798
Klement Sekeraf62ae122016-10-11 11:47:09 +0200799 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
800 """
801 Search the packet info list for the next packet info with same source
802 and destination interface indexes
803
804 :param src_index: source interface index to search for
805 :param dst_index: destination interface index to search for
806 :param info: packet info - where to start the search
807 :returns: packet info or None
808
809 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200810 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200811 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200812 if info is None:
813 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200814 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200815 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200816
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200817 def assert_equal(self, real_value, expected_value, name_or_class=None):
818 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100819 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200820 return
821 try:
822 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
823 msg = msg % (getdoc(name_or_class).strip(),
824 real_value, str(name_or_class(real_value)),
825 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100826 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200827 msg = "Invalid %s: %s does not match expected value %s" % (
828 name_or_class, real_value, expected_value)
829
830 self.assertEqual(real_value, expected_value, msg)
831
Klement Sekerab17dd962017-01-09 07:43:48 +0100832 def assert_in_range(self,
833 real_value,
834 expected_min,
835 expected_max,
836 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200837 if name is None:
838 msg = None
839 else:
840 msg = "Invalid %s: %s out of range <%s,%s>" % (
841 name, real_value, expected_min, expected_max)
842 self.assertTrue(expected_min <= real_value <= expected_max, msg)
843
Klement Sekerad81ae412018-05-16 10:52:54 +0200844 def assert_packet_checksums_valid(self, packet,
845 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200846 received = packet.__class__(str(packet))
847 self.logger.debug(
848 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200849 udp_layers = ['UDP', 'UDPerror']
850 checksum_fields = ['cksum', 'chksum']
851 checksums = []
852 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200853 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200854 while True:
855 layer = temp.getlayer(counter)
856 if layer:
857 for cf in checksum_fields:
858 if hasattr(layer, cf):
859 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100860 0 == getattr(layer, cf) and \
861 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200862 continue
863 delattr(layer, cf)
864 checksums.append((counter, cf))
865 else:
866 break
867 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200868 if 0 == len(checksums):
869 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200870 temp = temp.__class__(str(temp))
871 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200872 calc_sum = getattr(temp[layer], cf)
873 self.assert_equal(
874 getattr(received[layer], cf), calc_sum,
875 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
876 self.logger.debug(
877 "Checksum field `%s` on `%s` layer has correct value `%s`" %
878 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200879
880 def assert_checksum_valid(self, received_packet, layer,
881 field_name='chksum',
882 ignore_zero_checksum=False):
883 """ Check checksum of received packet on given layer """
884 received_packet_checksum = getattr(received_packet[layer], field_name)
885 if ignore_zero_checksum and 0 == received_packet_checksum:
886 return
887 recalculated = received_packet.__class__(str(received_packet))
888 delattr(recalculated[layer], field_name)
889 recalculated = recalculated.__class__(str(recalculated))
890 self.assert_equal(received_packet_checksum,
891 getattr(recalculated[layer], field_name),
892 "packet checksum on layer: %s" % layer)
893
894 def assert_ip_checksum_valid(self, received_packet,
895 ignore_zero_checksum=False):
896 self.assert_checksum_valid(received_packet, 'IP',
897 ignore_zero_checksum=ignore_zero_checksum)
898
899 def assert_tcp_checksum_valid(self, received_packet,
900 ignore_zero_checksum=False):
901 self.assert_checksum_valid(received_packet, 'TCP',
902 ignore_zero_checksum=ignore_zero_checksum)
903
904 def assert_udp_checksum_valid(self, received_packet,
905 ignore_zero_checksum=True):
906 self.assert_checksum_valid(received_packet, 'UDP',
907 ignore_zero_checksum=ignore_zero_checksum)
908
909 def assert_embedded_icmp_checksum_valid(self, received_packet):
910 if received_packet.haslayer(IPerror):
911 self.assert_checksum_valid(received_packet, 'IPerror')
912 if received_packet.haslayer(TCPerror):
913 self.assert_checksum_valid(received_packet, 'TCPerror')
914 if received_packet.haslayer(UDPerror):
915 self.assert_checksum_valid(received_packet, 'UDPerror',
916 ignore_zero_checksum=True)
917 if received_packet.haslayer(ICMPerror):
918 self.assert_checksum_valid(received_packet, 'ICMPerror')
919
920 def assert_icmp_checksum_valid(self, received_packet):
921 self.assert_checksum_valid(received_packet, 'ICMP')
922 self.assert_embedded_icmp_checksum_valid(received_packet)
923
924 def assert_icmpv6_checksum_valid(self, pkt):
925 if pkt.haslayer(ICMPv6DestUnreach):
926 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
927 self.assert_embedded_icmp_checksum_valid(pkt)
928 if pkt.haslayer(ICMPv6EchoRequest):
929 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
930 if pkt.haslayer(ICMPv6EchoReply):
931 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
932
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100933 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +0100934 if counter.startswith("/"):
935 counter_value = self.statistics.get_counter(counter)
936 self.assert_equal(counter_value, expected_value,
937 "packet counter `%s'" % counter)
938 else:
939 counters = self.vapi.cli("sh errors").split('\n')
940 counter_value = -1
941 for i in range(1, len(counters) - 1):
942 results = counters[i].split()
943 if results[1] == counter:
944 counter_value = int(results[0])
945 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100946
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100947 @classmethod
948 def sleep(cls, timeout, remark=None):
949 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800950 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000951 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100952 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000953 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800954 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200955 cls.logger.error("unexpected time.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800956 "slept for %es instead of ~%es!",
957 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000958 if hasattr(cls, 'logger'):
959 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800960 "Finished sleep (%s) - slept %es (wanted %es)",
961 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +0100962
Neale Ranns947ea622018-06-07 23:48:20 -0700963 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800964 self.vapi.cli("clear trace")
965 intf.add_stream(pkts)
966 self.pg_enable_capture(self.pg_interfaces)
967 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700968 if not timeout:
969 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800970 for i in self.pg_interfaces:
971 i.get_capture(0, timeout=timeout)
972 i.assert_nothing_captured(remark=remark)
973 timeout = 0.1
974
975 def send_and_expect(self, input, pkts, output):
976 self.vapi.cli("clear trace")
977 input.add_stream(pkts)
978 self.pg_enable_capture(self.pg_interfaces)
979 self.pg_start()
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700980 if isinstance(object, (list,)):
981 rx = []
982 for o in output:
983 rx.append(output.get_capture(len(pkts)))
984 else:
985 rx = output.get_capture(len(pkts))
986 return rx
987
988 def send_and_expect_only(self, input, pkts, output, timeout=None):
989 self.vapi.cli("clear trace")
990 input.add_stream(pkts)
991 self.pg_enable_capture(self.pg_interfaces)
992 self.pg_start()
993 if isinstance(object, (list,)):
994 outputs = output
995 rx = []
996 for o in outputs:
997 rx.append(output.get_capture(len(pkts)))
998 else:
999 rx = output.get_capture(len(pkts))
1000 outputs = [output]
1001 if not timeout:
1002 timeout = 1
1003 for i in self.pg_interfaces:
1004 if i not in outputs:
1005 i.get_capture(0, timeout=timeout)
1006 i.assert_nothing_captured()
1007 timeout = 0.1
1008
Neale Ranns52fae862018-01-08 04:41:42 -08001009 return rx
1010
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001011 def runTest(self):
1012 """ unittest calls runTest when TestCase is instantiated without a
1013 test case. Use case: Writing unittests against VppTestCase"""
1014 pass
1015
Damjan Marionf56b77a2016-10-03 19:44:57 +02001016
juraj.linkes184870a2018-07-16 14:22:01 +02001017def get_testcase_doc_name(test):
1018 return getdoc(test.__class__).splitlines()[0]
1019
1020
Ole Trøan5ba91592018-11-22 10:01:09 +00001021def get_test_description(descriptions, test):
1022 short_description = test.shortDescription()
1023 if descriptions and short_description:
1024 return short_description
1025 else:
1026 return str(test)
1027
1028
juraj.linkes40dd73b2018-09-21 13:55:16 +02001029class TestCaseInfo(object):
1030 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1031 self.logger = logger
1032 self.tempdir = tempdir
1033 self.vpp_pid = vpp_pid
1034 self.vpp_bin_path = vpp_bin_path
1035 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001036
1037
Damjan Marionf56b77a2016-10-03 19:44:57 +02001038class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001039 """
1040 @property result_string
1041 String variable to store the test case result string.
1042 @property errors
1043 List variable containing 2-tuples of TestCase instances and strings
1044 holding formatted tracebacks. Each tuple represents a test which
1045 raised an unexpected exception.
1046 @property failures
1047 List variable containing 2-tuples of TestCase instances and strings
1048 holding formatted tracebacks. Each tuple represents a test where
1049 a failure was explicitly signalled using the TestCase.assert*()
1050 methods.
1051 """
1052
juraj.linkes40dd73b2018-09-21 13:55:16 +02001053 failed_test_cases_info = set()
1054 core_crash_test_cases_info = set()
1055 current_test_case_info = None
1056
juraj.linkesabec0122018-11-16 17:28:56 +01001057 def __init__(self, stream, descriptions, verbosity, runner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001058 """
Klement Sekerada505f62017-01-04 12:58:53 +01001059 :param stream File descriptor to store where to report test results.
1060 Set to the standard error stream by default.
1061 :param descriptions Boolean variable to store information if to use
1062 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001063 :param verbosity Integer variable to store required verbosity level.
1064 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001065 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
1066 self.stream = stream
1067 self.descriptions = descriptions
1068 self.verbosity = verbosity
1069 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001070 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001071
Damjan Marionf56b77a2016-10-03 19:44:57 +02001072 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001073 """
1074 Record a test succeeded result
1075
1076 :param test:
1077
1078 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001079 if self.current_test_case_info:
1080 self.current_test_case_info.logger.debug(
1081 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1082 test._testMethodName,
1083 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001084 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001085 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001086
juraj.linkescae64f82018-09-19 15:01:47 +02001087 self.send_result_through_pipe(test, PASS)
1088
Klement Sekeraf62ae122016-10-11 11:47:09 +02001089 def addSkip(self, test, reason):
1090 """
1091 Record a test skipped.
1092
1093 :param test:
1094 :param reason:
1095
1096 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001097 if self.current_test_case_info:
1098 self.current_test_case_info.logger.debug(
1099 "--- addSkip() %s.%s(%s) called, reason is %s" %
1100 (test.__class__.__name__, test._testMethodName,
1101 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001102 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001103 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001104
juraj.linkescae64f82018-09-19 15:01:47 +02001105 self.send_result_through_pipe(test, SKIP)
1106
juraj.linkes40dd73b2018-09-21 13:55:16 +02001107 def symlink_failed(self):
1108 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001109 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001110 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001111 link_path = os.path.join(
1112 failed_dir,
1113 '%s-FAILED' %
1114 os.path.basename(self.current_test_case_info.tempdir))
1115 if self.current_test_case_info.logger:
1116 self.current_test_case_info.logger.debug(
1117 "creating a link to the failed test")
1118 self.current_test_case_info.logger.debug(
1119 "os.symlink(%s, %s)" %
1120 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001121 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001122 if self.current_test_case_info.logger:
1123 self.current_test_case_info.logger.debug(
1124 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001125 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001126 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001127
Klement Sekeraf413bef2017-08-15 07:09:02 +02001128 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001129 if self.current_test_case_info.logger:
1130 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001131
juraj.linkescae64f82018-09-19 15:01:47 +02001132 def send_result_through_pipe(self, test, result):
1133 if hasattr(self, 'test_framework_result_pipe'):
1134 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001135 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001136 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001137
juraj.linkes40dd73b2018-09-21 13:55:16 +02001138 def log_error(self, test, err, fn_name):
1139 if self.current_test_case_info:
1140 if isinstance(test, unittest.suite._ErrorHolder):
1141 test_name = test.description
1142 else:
1143 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1144 test._testMethodName,
1145 test._testMethodDoc)
1146 self.current_test_case_info.logger.debug(
1147 "--- %s() %s called, err is %s" %
1148 (fn_name, test_name, err))
1149 self.current_test_case_info.logger.debug(
1150 "formatted exception is:\n%s" %
1151 "".join(format_exception(*err)))
1152
1153 def add_error(self, test, err, unittest_fn, error_type):
1154 if error_type == FAIL:
1155 self.log_error(test, err, 'addFailure')
1156 error_type_str = colorize("FAIL", RED)
1157 elif error_type == ERROR:
1158 self.log_error(test, err, 'addError')
1159 error_type_str = colorize("ERROR", RED)
1160 else:
1161 raise Exception('Error type %s cannot be used to record an '
1162 'error or a failure' % error_type)
1163
1164 unittest_fn(self, test, err)
1165 if self.current_test_case_info:
1166 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1167 (error_type_str,
1168 self.current_test_case_info.tempdir)
1169 self.symlink_failed()
1170 self.failed_test_cases_info.add(self.current_test_case_info)
1171 if is_core_present(self.current_test_case_info.tempdir):
1172 if not self.current_test_case_info.core_crash_test:
1173 if isinstance(test, unittest.suite._ErrorHolder):
1174 test_name = str(test)
1175 else:
1176 test_name = "'{}' ({})".format(
1177 get_testcase_doc_name(test), test.id())
1178 self.current_test_case_info.core_crash_test = test_name
1179 self.core_crash_test_cases_info.add(
1180 self.current_test_case_info)
1181 else:
1182 self.result_string = '%s [no temp dir]' % error_type_str
1183
1184 self.send_result_through_pipe(test, error_type)
1185
Damjan Marionf56b77a2016-10-03 19:44:57 +02001186 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001187 """
1188 Record a test failed result
1189
1190 :param test:
1191 :param err: error message
1192
1193 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001194 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001195
Damjan Marionf56b77a2016-10-03 19:44:57 +02001196 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001197 """
1198 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001199
Klement Sekeraf62ae122016-10-11 11:47:09 +02001200 :param test:
1201 :param err: error message
1202
1203 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001204 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001205
Damjan Marionf56b77a2016-10-03 19:44:57 +02001206 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001207 """
1208 Get test description
1209
1210 :param test:
1211 :returns: test description
1212
1213 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001214 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001215
Damjan Marionf56b77a2016-10-03 19:44:57 +02001216 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001217 """
1218 Start a test
1219
1220 :param test:
1221
1222 """
Paul Vinciguerra86ebba62018-11-21 09:28:32 -08001223 test.print_header()
juraj.linkes40dd73b2018-09-21 13:55:16 +02001224
Damjan Marionf56b77a2016-10-03 19:44:57 +02001225 unittest.TestResult.startTest(self, test)
1226 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001227 self.stream.writeln(
1228 "Starting " + self.getDescription(test) + " ...")
1229 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001230
Damjan Marionf56b77a2016-10-03 19:44:57 +02001231 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001232 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001233 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001234
1235 :param test:
1236
1237 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001238 unittest.TestResult.stopTest(self, test)
1239 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001240 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001241 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001242 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001243 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001244 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001245 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001246 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001247
1248 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001249
Damjan Marionf56b77a2016-10-03 19:44:57 +02001250 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001251 """
1252 Print errors from running the test case
1253 """
juraj.linkesabec0122018-11-16 17:28:56 +01001254 if len(self.errors) > 0 or len(self.failures) > 0:
1255 self.stream.writeln()
1256 self.printErrorList('ERROR', self.errors)
1257 self.printErrorList('FAIL', self.failures)
1258
1259 # ^^ that is the last output from unittest before summary
1260 if not self.runner.print_summary:
1261 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1262 self.stream = devnull
1263 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001264
Damjan Marionf56b77a2016-10-03 19:44:57 +02001265 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001266 """
1267 Print error list to the output stream together with error type
1268 and test case description.
1269
1270 :param flavour: error type
1271 :param errors: iterable errors
1272
1273 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001274 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001275 self.stream.writeln(double_line_delim)
1276 self.stream.writeln("%s: %s" %
1277 (flavour, self.getDescription(test)))
1278 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001279 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001280
1281
Damjan Marionf56b77a2016-10-03 19:44:57 +02001282class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001283 """
Klement Sekera104543f2017-02-03 07:29:43 +01001284 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001285 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001286
Klement Sekeraf62ae122016-10-11 11:47:09 +02001287 @property
1288 def resultclass(self):
1289 """Class maintaining the results of the tests"""
1290 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001291
juraj.linkes184870a2018-07-16 14:22:01 +02001292 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001293 result_pipe=None, failfast=False, buffer=False,
juraj.linkesabec0122018-11-16 17:28:56 +01001294 resultclass=None, print_summary=True):
Klement Sekera7a161da2017-01-17 13:42:48 +01001295 # ignore stream setting here, use hard-coded stdout to be in sync
1296 # with prints from VppTestCase methods ...
1297 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1298 verbosity, failfast, buffer,
1299 resultclass)
juraj.linkesccfead62018-11-21 13:20:43 +01001300 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001301
juraj.linkesabec0122018-11-16 17:28:56 +01001302 self.orig_stream = self.stream
1303 self.resultclass.test_framework_result_pipe = result_pipe
1304
1305 self.print_summary = print_summary
1306
1307 def _makeResult(self):
1308 return self.resultclass(self.stream,
1309 self.descriptions,
1310 self.verbosity,
1311 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001312
Damjan Marionf56b77a2016-10-03 19:44:57 +02001313 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001314 """
1315 Run the tests
1316
1317 :param test:
1318
1319 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001320 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001321
1322 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001323 if not self.print_summary:
1324 self.stream = self.orig_stream
1325 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001326 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001327
1328
1329class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001330 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001331 self.logger = logger
1332 self.args = args
1333 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001334 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001335 super(Worker, self).__init__()
1336
1337 def run(self):
1338 executable = self.args[0]
1339 self.logger.debug("Running executable w/args `%s'" % self.args)
1340 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001341 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001342 env["CK_LOG_FILE_NAME"] = "-"
1343 self.process = subprocess.Popen(
1344 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1345 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1346 out, err = self.process.communicate()
1347 self.logger.debug("Finished running `%s'" % executable)
1348 self.logger.info("Return code is `%s'" % self.process.returncode)
1349 self.logger.info(single_line_delim)
1350 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1351 self.logger.info(single_line_delim)
1352 self.logger.info(out)
1353 self.logger.info(single_line_delim)
1354 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1355 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001356 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001357 self.logger.info(single_line_delim)
1358 self.result = self.process.returncode