blob: 2c1c8291ed14e0f11af16d755cdd393deaaa9a63 [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
Damjan Marionf56b77a2016-10-03 19:44:57 +0200276 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200277 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100278 s = os.getenv("STEP", "n")
279 cls.step = True if s.lower() in ("y", "yes", "1") else False
280 d = os.getenv("DEBUG", None)
281 c = os.getenv("CACHE_OUTPUT", "1")
282 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200283 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100284 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
285 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100286 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
287 plugin_path = None
288 if cls.plugin_path is not None:
289 if cls.extern_plugin_path is not None:
290 plugin_path = "%s:%s" % (
291 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100292 else:
293 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100294 elif cls.extern_plugin_path is not None:
295 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100296 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100297 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100298 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100299 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100300 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100301 if size is not None:
302 coredump_size = "coredump-size %s" % size
303 if coredump_size is None:
304 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200305
Ole Troana45dc072018-12-21 16:04:22 +0100306 cpu_core_number = cls.get_least_used_cpu()
juraj.linkes184870a2018-07-16 14:22:01 +0200307
Ole Troana45dc072018-12-21 16:04:22 +0100308 cls.vpp_cmdline = [cls.vpp_bin, "unix",
309 "{", "nodaemon", debug_cli, "full-coredump",
310 coredump_size, "runtime-dir", cls.tempdir, "}",
311 "api-trace", "{", "on", "}", "api-segment", "{",
312 "prefix", cls.shm_prefix, "}", "cpu", "{",
313 "main-core", str(cpu_core_number), "}", "statseg",
314 "{", "socket-name", cls.stats_sock, "}", "plugins",
315 "{", "plugin", "dpdk_plugin.so", "{", "disable",
316 "}", "plugin", "unittest_plugin.so", "{", "enable",
317 "}"] + cls.extra_vpp_plugin_config + ["}", ]
318 if cls.extra_vpp_punt_config is not None:
319 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100320 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100321 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100322 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
323 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200324
325 @classmethod
326 def wait_for_enter(cls):
327 if cls.debug_gdbserver:
328 print(double_line_delim)
329 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
330 elif cls.debug_gdb:
331 print(double_line_delim)
332 print("Spawned VPP with PID: %d" % cls.vpp.pid)
333 else:
334 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
335 return
336 print(single_line_delim)
337 print("You can debug the VPP using e.g.:")
338 if cls.debug_gdbserver:
339 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
340 print("Now is the time to attach a gdb by running the above "
341 "command, set up breakpoints etc. and then resume VPP from "
342 "within gdb by issuing the 'continue' command")
343 elif cls.debug_gdb:
344 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
345 print("Now is the time to attach a gdb by running the above "
346 "command and set up breakpoints etc.")
347 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800348 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200349
350 @classmethod
351 def run_vpp(cls):
352 cmdline = cls.vpp_cmdline
353
354 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100355 gdbserver = '/usr/bin/gdbserver'
356 if not os.path.isfile(gdbserver) or \
357 not os.access(gdbserver, os.X_OK):
358 raise Exception("gdbserver binary '%s' does not exist or is "
359 "not executable" % gdbserver)
360
361 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200362 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
363
Klement Sekera931be3a2016-11-03 05:36:01 +0100364 try:
365 cls.vpp = subprocess.Popen(cmdline,
366 stdout=subprocess.PIPE,
367 stderr=subprocess.PIPE,
368 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800369 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800370 cls.logger.critical("Subprocess returned with non-0 return code: ("
371 "%s)", e.returncode)
372 raise
373 except OSError as e:
374 cls.logger.critical("Subprocess returned with OS error: "
375 "(%s) %s", e.errno, e.strerror)
376 raise
377 except Exception as e:
378 cls.logger.exception("Subprocess returned unexpected from "
379 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100380 raise
381
Klement Sekera277b89c2016-10-28 13:20:27 +0200382 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100383
Damjan Marionf56b77a2016-10-03 19:44:57 +0200384 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200385 def wait_for_stats_socket(cls):
386 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800387 ok = False
388 while time.time() < deadline or \
389 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200390 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800391 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200392 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700393 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800394 if not ok:
395 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200396
397 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200398 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200399 """
400 Perform class setup before running the testcase
401 Remove shared memory files, start vpp and connect the vpp-api
402 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800403 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100404 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100405 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100406 cls.logger = get_logger(cls.__name__)
407 if hasattr(cls, 'parallel_handler'):
408 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100409 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200410 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200411 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200412 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200413 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
414 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100415 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
416 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200417 cls.file_handler.setLevel(DEBUG)
418 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200419 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200420 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200421 cls.logger.info("Temporary dir is %s, shm prefix is %s",
422 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200423 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100424 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100425 cls._captures = []
426 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200427 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100428 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100429 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200430 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200431 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200432 # need to catch exceptions here because if we raise, then the cleanup
433 # doesn't get called and we might end with a zombie vpp
434 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200435 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200436 cls.reporter.send_keep_alive(cls, 'setUpClass')
437 VppTestResult.current_test_case_info = TestCaseInfo(
438 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100439 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100440 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100441 cls.pump_thread_stop_flag = Event()
442 cls.pump_thread_wakeup_pipe = os.pipe()
443 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100444 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100445 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200446 if cls.debug_gdb or cls.debug_gdbserver:
447 read_timeout = 0
448 else:
449 read_timeout = 5
450 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
451 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100452 if cls.step:
453 hook = StepHook(cls)
454 else:
455 hook = PollHook(cls)
456 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200457 cls.wait_for_stats_socket()
458 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200459 try:
460 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100461 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200462 cls.vpp_startup_failed = True
463 cls.logger.critical(
464 "VPP died shortly after startup, check the"
465 " output to standard error for possible cause")
466 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100467 try:
468 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100469 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100470 try:
471 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100472 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100473 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100474 if cls.debug_gdbserver:
475 print(colorize("You're running VPP inside gdbserver but "
476 "VPP-API connection failed, did you forget "
477 "to 'continue' VPP from within gdb?", RED))
478 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100479 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100480 try:
481 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100482 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100483 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100484 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200485
Damjan Marionf56b77a2016-10-03 19:44:57 +0200486 @classmethod
487 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200488 """
489 Disconnect vpp-api, kill vpp and cleanup shared memory files
490 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200491 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
492 cls.vpp.poll()
493 if cls.vpp.returncode is None:
494 print(double_line_delim)
495 print("VPP or GDB server is still running")
496 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800497 input("When done debugging, press ENTER to kill the "
498 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200499
juraj.linkes184870a2018-07-16 14:22:01 +0200500 # first signal that we want to stop the pump thread, then wake it up
501 if hasattr(cls, 'pump_thread_stop_flag'):
502 cls.pump_thread_stop_flag.set()
503 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100504 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100505 if hasattr(cls, 'pump_thread'):
506 cls.logger.debug("Waiting for pump thread to stop")
507 cls.pump_thread.join()
508 if hasattr(cls, 'vpp_stderr_reader_thread'):
509 cls.logger.debug("Waiting for stdderr pump to stop")
510 cls.vpp_stderr_reader_thread.join()
511
Klement Sekeraf62ae122016-10-11 11:47:09 +0200512 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100513 if hasattr(cls, 'vapi'):
514 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100515 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200516 cls.vpp.poll()
517 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100518 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200519 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100520 cls.logger.debug("Waiting for vpp to die")
521 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200522 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200523
Klement Sekera3747c752017-04-10 06:30:17 +0200524 if cls.vpp_startup_failed:
525 stdout_log = cls.logger.info
526 stderr_log = cls.logger.critical
527 else:
528 stdout_log = cls.logger.info
529 stderr_log = cls.logger.info
530
Klement Sekerae4504c62016-12-08 10:16:41 +0100531 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200532 stdout_log(single_line_delim)
533 stdout_log('VPP output to stdout while running %s:', cls.__name__)
534 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100535 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200536 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
537 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200538 stdout_log('\n%s', vpp_output)
539 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200540
Klement Sekerae4504c62016-12-08 10:16:41 +0100541 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200542 stderr_log(single_line_delim)
543 stderr_log('VPP output to stderr while running %s:', cls.__name__)
544 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100545 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200546 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
547 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200548 stderr_log('\n%s', vpp_output)
549 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200550
Damjan Marionf56b77a2016-10-03 19:44:57 +0200551 @classmethod
552 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200553 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200554 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200555 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200556 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100557 cls.reset_packet_infos()
558 if debug_framework:
559 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200560
Damjan Marionf56b77a2016-10-03 19:44:57 +0200561 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200562 """ Show various debug prints after each test """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800563 super(VppTestCase, self).tearDown()
Klement Sekerab91017a2017-02-09 06:04:36 +0100564 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
565 (self.__class__.__name__, self._testMethodName,
566 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200567 if not self.vpp_dead:
Klement Sekerad91fa612019-01-15 13:25:09 +0100568 self.logger.debug(self.vapi.cli("show trace max 1000"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700569 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200570 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200571 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200572 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800573 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100574 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500575 # Save/Dump VPP api trace log
576 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
577 tmp_api_trace = "/tmp/%s" % api_trace
578 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
579 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
580 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
581 vpp_api_trace_log))
582 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500583 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500584 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100585 else:
586 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200587
Damjan Marionf56b77a2016-10-03 19:44:57 +0200588 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200589 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800590 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200591 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100592 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
593 (self.__class__.__name__, self._testMethodName,
594 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100595 if self.vpp_dead:
596 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100597 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100598 self.vpp_stdout_deque.append(
599 "--- test setUp() for %s.%s(%s) starts here ---\n" %
600 (self.__class__.__name__, self._testMethodName,
601 self._testMethodDoc))
602 self.vpp_stderr_deque.append(
603 "--- test setUp() for %s.%s(%s) starts here ---\n" %
604 (self.__class__.__name__, self._testMethodName,
605 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200606 self.vapi.cli("clear trace")
607 # store the test instance inside the test class - so that objects
608 # holding the class can access instance methods (like assertEqual)
609 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200610
Damjan Marionf56b77a2016-10-03 19:44:57 +0200611 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200612 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200613 """
614 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200615
Klement Sekera75e7d132017-09-20 08:26:30 +0200616 :param interfaces: iterable interface indexes (if None,
617 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200618
Klement Sekeraf62ae122016-10-11 11:47:09 +0200619 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200620 if interfaces is None:
621 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200622 for i in interfaces:
623 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200624
Damjan Marionf56b77a2016-10-03 19:44:57 +0200625 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100626 def register_capture(cls, cap_name):
627 """ Register a capture in the testclass """
628 # add to the list of captures with current timestamp
629 cls._captures.append((time.time(), cap_name))
630 # filter out from zombies
631 cls._zombie_captures = [(stamp, name)
632 for (stamp, name) in cls._zombie_captures
633 if name != cap_name]
634
635 @classmethod
636 def pg_start(cls):
637 """ Remove any zombie captures and enable the packet generator """
638 # how long before capture is allowed to be deleted - otherwise vpp
639 # crashes - 100ms seems enough (this shouldn't be needed at all)
640 capture_ttl = 0.1
641 now = time.time()
642 for stamp, cap_name in cls._zombie_captures:
643 wait = stamp + capture_ttl - now
644 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100645 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100646 now = time.time()
647 cls.logger.debug("Removing zombie capture %s" % cap_name)
648 cls.vapi.cli('packet-generator delete %s' % cap_name)
649
Klement Sekerad91fa612019-01-15 13:25:09 +0100650 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200651 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100652 cls._zombie_captures = cls._captures
653 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200654
Damjan Marionf56b77a2016-10-03 19:44:57 +0200655 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200656 def create_pg_interfaces(cls, interfaces):
657 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100658 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200659
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100660 :param interfaces: iterable indexes of the interfaces.
661 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200662
Klement Sekeraf62ae122016-10-11 11:47:09 +0200663 """
664 result = []
665 for i in interfaces:
666 intf = VppPGInterface(cls, i)
667 setattr(cls, intf.name, intf)
668 result.append(intf)
669 cls.pg_interfaces = result
670 return result
671
Matej Klotton0178d522016-11-04 11:11:44 +0100672 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200673 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100674 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100675 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100676
Klement Sekerab9ef2732018-06-24 22:49:33 +0200677 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100678 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100679 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200680 result = [VppLoInterface(cls) for i in range(count)]
681 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100682 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100683 cls.lo_interfaces = result
684 return result
685
Damjan Marionf56b77a2016-10-03 19:44:57 +0200686 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200687 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200688 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200689 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200690 NOTE: Currently works only when Raw layer is present.
691
692 :param packet: packet
693 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200694 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200695
696 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200697 packet_len = len(packet) + 4
698 extend = size - packet_len
699 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200700 num = (extend / len(padding)) + 1
701 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200702
Klement Sekeradab231a2016-12-21 08:50:14 +0100703 @classmethod
704 def reset_packet_infos(cls):
705 """ Reset the list of packet info objects and packet counts to zero """
706 cls._packet_infos = {}
707 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200708
Klement Sekeradab231a2016-12-21 08:50:14 +0100709 @classmethod
710 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200711 """
712 Create packet info object containing the source and destination indexes
713 and add it to the testcase's packet info list
714
Klement Sekeradab231a2016-12-21 08:50:14 +0100715 :param VppInterface src_if: source interface
716 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200717
718 :returns: _PacketInfo object
719
720 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200721 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100722 info.index = len(cls._packet_infos)
723 info.src = src_if.sw_if_index
724 info.dst = dst_if.sw_if_index
725 if isinstance(dst_if, VppSubInterface):
726 dst_idx = dst_if.parent.sw_if_index
727 else:
728 dst_idx = dst_if.sw_if_index
729 if dst_idx in cls._packet_count_for_dst_if_idx:
730 cls._packet_count_for_dst_if_idx[dst_idx] += 1
731 else:
732 cls._packet_count_for_dst_if_idx[dst_idx] = 1
733 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200734 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200735
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736 @staticmethod
737 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200738 """
739 Convert _PacketInfo object to packet payload
740
741 :param info: _PacketInfo object
742
743 :returns: string containing serialized data from packet info
744 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100745 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
746 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200747
Damjan Marionf56b77a2016-10-03 19:44:57 +0200748 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800749 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200750 """
751 Convert packet payload to _PacketInfo object
752
753 :param payload: packet payload
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800754 :type: <class 'scapy.packet.Raw'>
755 :param: payload_field: packet fieldname of payload "load" for
756 <class 'scapy.packet.Raw'>
Klement Sekeraf62ae122016-10-11 11:47:09 +0200757 :returns: _PacketInfo object containing de-serialized data from payload
758
759 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800760 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200761 info = _PacketInfo()
762 info.index = int(numbers[0])
763 info.src = int(numbers[1])
764 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100765 info.ip = int(numbers[3])
766 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200767 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200768
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200770 """
771 Iterate over the packet info list stored in the testcase
772 Start iteration with first element if info is None
773 Continue based on index in info if info is specified
774
775 :param info: info or None
776 :returns: next info in list or None if no more infos
777 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200778 if info is None:
779 next_index = 0
780 else:
781 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100782 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200783 return None
784 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100785 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200786
Klement Sekeraf62ae122016-10-11 11:47:09 +0200787 def get_next_packet_info_for_interface(self, src_index, info):
788 """
789 Search the packet info list for the next packet info with same source
790 interface index
791
792 :param src_index: source interface index to search for
793 :param info: packet info - where to start the search
794 :returns: packet info or None
795
796 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200797 while True:
798 info = self.get_next_packet_info(info)
799 if info is None:
800 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200801 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200802 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200803
Klement Sekeraf62ae122016-10-11 11:47:09 +0200804 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
805 """
806 Search the packet info list for the next packet info with same source
807 and destination interface indexes
808
809 :param src_index: source interface index to search for
810 :param dst_index: destination interface index to search for
811 :param info: packet info - where to start the search
812 :returns: packet info or None
813
814 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200815 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200816 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200817 if info is None:
818 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200819 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200820 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200821
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200822 def assert_equal(self, real_value, expected_value, name_or_class=None):
823 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100824 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200825 return
826 try:
827 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
828 msg = msg % (getdoc(name_or_class).strip(),
829 real_value, str(name_or_class(real_value)),
830 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100831 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200832 msg = "Invalid %s: %s does not match expected value %s" % (
833 name_or_class, real_value, expected_value)
834
835 self.assertEqual(real_value, expected_value, msg)
836
Klement Sekerab17dd962017-01-09 07:43:48 +0100837 def assert_in_range(self,
838 real_value,
839 expected_min,
840 expected_max,
841 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200842 if name is None:
843 msg = None
844 else:
845 msg = "Invalid %s: %s out of range <%s,%s>" % (
846 name, real_value, expected_min, expected_max)
847 self.assertTrue(expected_min <= real_value <= expected_max, msg)
848
Klement Sekerad81ae412018-05-16 10:52:54 +0200849 def assert_packet_checksums_valid(self, packet,
850 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200851 received = packet.__class__(str(packet))
852 self.logger.debug(
853 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200854 udp_layers = ['UDP', 'UDPerror']
855 checksum_fields = ['cksum', 'chksum']
856 checksums = []
857 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200858 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200859 while True:
860 layer = temp.getlayer(counter)
861 if layer:
862 for cf in checksum_fields:
863 if hasattr(layer, cf):
864 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100865 0 == getattr(layer, cf) and \
866 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200867 continue
868 delattr(layer, cf)
869 checksums.append((counter, cf))
870 else:
871 break
872 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200873 if 0 == len(checksums):
874 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200875 temp = temp.__class__(str(temp))
876 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200877 calc_sum = getattr(temp[layer], cf)
878 self.assert_equal(
879 getattr(received[layer], cf), calc_sum,
880 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
881 self.logger.debug(
882 "Checksum field `%s` on `%s` layer has correct value `%s`" %
883 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200884
885 def assert_checksum_valid(self, received_packet, layer,
886 field_name='chksum',
887 ignore_zero_checksum=False):
888 """ Check checksum of received packet on given layer """
889 received_packet_checksum = getattr(received_packet[layer], field_name)
890 if ignore_zero_checksum and 0 == received_packet_checksum:
891 return
892 recalculated = received_packet.__class__(str(received_packet))
893 delattr(recalculated[layer], field_name)
894 recalculated = recalculated.__class__(str(recalculated))
895 self.assert_equal(received_packet_checksum,
896 getattr(recalculated[layer], field_name),
897 "packet checksum on layer: %s" % layer)
898
899 def assert_ip_checksum_valid(self, received_packet,
900 ignore_zero_checksum=False):
901 self.assert_checksum_valid(received_packet, 'IP',
902 ignore_zero_checksum=ignore_zero_checksum)
903
904 def assert_tcp_checksum_valid(self, received_packet,
905 ignore_zero_checksum=False):
906 self.assert_checksum_valid(received_packet, 'TCP',
907 ignore_zero_checksum=ignore_zero_checksum)
908
909 def assert_udp_checksum_valid(self, received_packet,
910 ignore_zero_checksum=True):
911 self.assert_checksum_valid(received_packet, 'UDP',
912 ignore_zero_checksum=ignore_zero_checksum)
913
914 def assert_embedded_icmp_checksum_valid(self, received_packet):
915 if received_packet.haslayer(IPerror):
916 self.assert_checksum_valid(received_packet, 'IPerror')
917 if received_packet.haslayer(TCPerror):
918 self.assert_checksum_valid(received_packet, 'TCPerror')
919 if received_packet.haslayer(UDPerror):
920 self.assert_checksum_valid(received_packet, 'UDPerror',
921 ignore_zero_checksum=True)
922 if received_packet.haslayer(ICMPerror):
923 self.assert_checksum_valid(received_packet, 'ICMPerror')
924
925 def assert_icmp_checksum_valid(self, received_packet):
926 self.assert_checksum_valid(received_packet, 'ICMP')
927 self.assert_embedded_icmp_checksum_valid(received_packet)
928
929 def assert_icmpv6_checksum_valid(self, pkt):
930 if pkt.haslayer(ICMPv6DestUnreach):
931 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
932 self.assert_embedded_icmp_checksum_valid(pkt)
933 if pkt.haslayer(ICMPv6EchoRequest):
934 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
935 if pkt.haslayer(ICMPv6EchoReply):
936 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
937
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100938 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +0100939 if counter.startswith("/"):
940 counter_value = self.statistics.get_counter(counter)
941 self.assert_equal(counter_value, expected_value,
942 "packet counter `%s'" % counter)
943 else:
944 counters = self.vapi.cli("sh errors").split('\n')
945 counter_value = -1
946 for i in range(1, len(counters) - 1):
947 results = counters[i].split()
948 if results[1] == counter:
949 counter_value = int(results[0])
950 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100951
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100952 @classmethod
953 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700954
955 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
956 # * by Guido, only the main thread can be interrupted.
957 # */
958 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
959 if timeout == 0:
960 # yield quantum
961 if hasattr(os, 'sched_yield'):
962 os.sched_yield()
963 else:
964 time.sleep(0)
965 return
966
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100967 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800968 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000969 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100970 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000971 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800972 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700973 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800974 "slept for %es instead of ~%es!",
975 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000976 if hasattr(cls, 'logger'):
977 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800978 "Finished sleep (%s) - slept %es (wanted %es)",
979 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +0100980
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800981 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -0800982 self.vapi.cli("clear trace")
983 intf.add_stream(pkts)
984 self.pg_enable_capture(self.pg_interfaces)
985 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800986
987 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
988 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -0700989 if not timeout:
990 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800991 for i in self.pg_interfaces:
992 i.get_capture(0, timeout=timeout)
993 i.assert_nothing_captured(remark=remark)
994 timeout = 0.1
995
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800996 def send_and_expect(self, intf, pkts, output):
997 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -0800998 rx = output.get_capture(len(pkts))
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700999 return rx
1000
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001001 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1002 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001003 rx = output.get_capture(len(pkts))
1004 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001005 if not timeout:
1006 timeout = 1
1007 for i in self.pg_interfaces:
1008 if i not in outputs:
1009 i.get_capture(0, timeout=timeout)
1010 i.assert_nothing_captured()
1011 timeout = 0.1
1012
Neale Ranns52fae862018-01-08 04:41:42 -08001013 return rx
1014
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001015 def runTest(self):
1016 """ unittest calls runTest when TestCase is instantiated without a
1017 test case. Use case: Writing unittests against VppTestCase"""
1018 pass
1019
Damjan Marionf56b77a2016-10-03 19:44:57 +02001020
juraj.linkes184870a2018-07-16 14:22:01 +02001021def get_testcase_doc_name(test):
1022 return getdoc(test.__class__).splitlines()[0]
1023
1024
Ole Trøan5ba91592018-11-22 10:01:09 +00001025def get_test_description(descriptions, test):
1026 short_description = test.shortDescription()
1027 if descriptions and short_description:
1028 return short_description
1029 else:
1030 return str(test)
1031
1032
juraj.linkes40dd73b2018-09-21 13:55:16 +02001033class TestCaseInfo(object):
1034 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1035 self.logger = logger
1036 self.tempdir = tempdir
1037 self.vpp_pid = vpp_pid
1038 self.vpp_bin_path = vpp_bin_path
1039 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001040
1041
Damjan Marionf56b77a2016-10-03 19:44:57 +02001042class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001043 """
1044 @property result_string
1045 String variable to store the test case result string.
1046 @property errors
1047 List variable containing 2-tuples of TestCase instances and strings
1048 holding formatted tracebacks. Each tuple represents a test which
1049 raised an unexpected exception.
1050 @property failures
1051 List variable containing 2-tuples of TestCase instances and strings
1052 holding formatted tracebacks. Each tuple represents a test where
1053 a failure was explicitly signalled using the TestCase.assert*()
1054 methods.
1055 """
1056
juraj.linkes40dd73b2018-09-21 13:55:16 +02001057 failed_test_cases_info = set()
1058 core_crash_test_cases_info = set()
1059 current_test_case_info = None
1060
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001061 def __init__(self, stream=None, descriptions=None, verbosity=None,
1062 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001063 """
Klement Sekerada505f62017-01-04 12:58:53 +01001064 :param stream File descriptor to store where to report test results.
1065 Set to the standard error stream by default.
1066 :param descriptions Boolean variable to store information if to use
1067 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001068 :param verbosity Integer variable to store required verbosity level.
1069 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001070 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001071 self.stream = stream
1072 self.descriptions = descriptions
1073 self.verbosity = verbosity
1074 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001075 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001076
Damjan Marionf56b77a2016-10-03 19:44:57 +02001077 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001078 """
1079 Record a test succeeded result
1080
1081 :param test:
1082
1083 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001084 if self.current_test_case_info:
1085 self.current_test_case_info.logger.debug(
1086 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1087 test._testMethodName,
1088 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001089 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001090 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001091
juraj.linkescae64f82018-09-19 15:01:47 +02001092 self.send_result_through_pipe(test, PASS)
1093
Klement Sekeraf62ae122016-10-11 11:47:09 +02001094 def addSkip(self, test, reason):
1095 """
1096 Record a test skipped.
1097
1098 :param test:
1099 :param reason:
1100
1101 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001102 if self.current_test_case_info:
1103 self.current_test_case_info.logger.debug(
1104 "--- addSkip() %s.%s(%s) called, reason is %s" %
1105 (test.__class__.__name__, test._testMethodName,
1106 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001107 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001108 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001109
juraj.linkescae64f82018-09-19 15:01:47 +02001110 self.send_result_through_pipe(test, SKIP)
1111
juraj.linkes40dd73b2018-09-21 13:55:16 +02001112 def symlink_failed(self):
1113 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001114 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001115 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001116 link_path = os.path.join(
1117 failed_dir,
1118 '%s-FAILED' %
1119 os.path.basename(self.current_test_case_info.tempdir))
1120 if self.current_test_case_info.logger:
1121 self.current_test_case_info.logger.debug(
1122 "creating a link to the failed test")
1123 self.current_test_case_info.logger.debug(
1124 "os.symlink(%s, %s)" %
1125 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001126 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001127 if self.current_test_case_info.logger:
1128 self.current_test_case_info.logger.debug(
1129 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001130 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001131 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001132
Klement Sekeraf413bef2017-08-15 07:09:02 +02001133 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001134 if self.current_test_case_info.logger:
1135 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001136
juraj.linkescae64f82018-09-19 15:01:47 +02001137 def send_result_through_pipe(self, test, result):
1138 if hasattr(self, 'test_framework_result_pipe'):
1139 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001140 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001141 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001142
juraj.linkes40dd73b2018-09-21 13:55:16 +02001143 def log_error(self, test, err, fn_name):
1144 if self.current_test_case_info:
1145 if isinstance(test, unittest.suite._ErrorHolder):
1146 test_name = test.description
1147 else:
1148 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1149 test._testMethodName,
1150 test._testMethodDoc)
1151 self.current_test_case_info.logger.debug(
1152 "--- %s() %s called, err is %s" %
1153 (fn_name, test_name, err))
1154 self.current_test_case_info.logger.debug(
1155 "formatted exception is:\n%s" %
1156 "".join(format_exception(*err)))
1157
1158 def add_error(self, test, err, unittest_fn, error_type):
1159 if error_type == FAIL:
1160 self.log_error(test, err, 'addFailure')
1161 error_type_str = colorize("FAIL", RED)
1162 elif error_type == ERROR:
1163 self.log_error(test, err, 'addError')
1164 error_type_str = colorize("ERROR", RED)
1165 else:
1166 raise Exception('Error type %s cannot be used to record an '
1167 'error or a failure' % error_type)
1168
1169 unittest_fn(self, test, err)
1170 if self.current_test_case_info:
1171 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1172 (error_type_str,
1173 self.current_test_case_info.tempdir)
1174 self.symlink_failed()
1175 self.failed_test_cases_info.add(self.current_test_case_info)
1176 if is_core_present(self.current_test_case_info.tempdir):
1177 if not self.current_test_case_info.core_crash_test:
1178 if isinstance(test, unittest.suite._ErrorHolder):
1179 test_name = str(test)
1180 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001181 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001182 get_testcase_doc_name(test), test.id())
1183 self.current_test_case_info.core_crash_test = test_name
1184 self.core_crash_test_cases_info.add(
1185 self.current_test_case_info)
1186 else:
1187 self.result_string = '%s [no temp dir]' % error_type_str
1188
1189 self.send_result_through_pipe(test, error_type)
1190
Damjan Marionf56b77a2016-10-03 19:44:57 +02001191 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001192 """
1193 Record a test failed result
1194
1195 :param test:
1196 :param err: error message
1197
1198 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001199 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001200
Damjan Marionf56b77a2016-10-03 19:44:57 +02001201 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001202 """
1203 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001204
Klement Sekeraf62ae122016-10-11 11:47:09 +02001205 :param test:
1206 :param err: error message
1207
1208 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001209 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001210
Damjan Marionf56b77a2016-10-03 19:44:57 +02001211 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001212 """
1213 Get test description
1214
1215 :param test:
1216 :returns: test description
1217
1218 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001219 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001220
Damjan Marionf56b77a2016-10-03 19:44:57 +02001221 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001222 """
1223 Start a test
1224
1225 :param test:
1226
1227 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001228
1229 def print_header(test):
1230 if not hasattr(test.__class__, '_header_printed'):
1231 print(double_line_delim)
1232 print(colorize(getdoc(test).splitlines()[0], GREEN))
1233 print(double_line_delim)
1234 test.__class__._header_printed = True
1235
1236 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001237
Damjan Marionf56b77a2016-10-03 19:44:57 +02001238 unittest.TestResult.startTest(self, test)
1239 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001240 self.stream.writeln(
1241 "Starting " + self.getDescription(test) + " ...")
1242 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001243
Damjan Marionf56b77a2016-10-03 19:44:57 +02001244 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001245 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001246 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001247
1248 :param test:
1249
1250 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001251 unittest.TestResult.stopTest(self, test)
1252 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001253 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001254 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001255 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001256 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001257 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001258 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001259 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001260
1261 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001262
Damjan Marionf56b77a2016-10-03 19:44:57 +02001263 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001264 """
1265 Print errors from running the test case
1266 """
juraj.linkesabec0122018-11-16 17:28:56 +01001267 if len(self.errors) > 0 or len(self.failures) > 0:
1268 self.stream.writeln()
1269 self.printErrorList('ERROR', self.errors)
1270 self.printErrorList('FAIL', self.failures)
1271
1272 # ^^ that is the last output from unittest before summary
1273 if not self.runner.print_summary:
1274 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1275 self.stream = devnull
1276 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001277
Damjan Marionf56b77a2016-10-03 19:44:57 +02001278 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001279 """
1280 Print error list to the output stream together with error type
1281 and test case description.
1282
1283 :param flavour: error type
1284 :param errors: iterable errors
1285
1286 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001287 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001288 self.stream.writeln(double_line_delim)
1289 self.stream.writeln("%s: %s" %
1290 (flavour, self.getDescription(test)))
1291 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001292 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001293
1294
Damjan Marionf56b77a2016-10-03 19:44:57 +02001295class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001296 """
Klement Sekera104543f2017-02-03 07:29:43 +01001297 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001298 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001299
Klement Sekeraf62ae122016-10-11 11:47:09 +02001300 @property
1301 def resultclass(self):
1302 """Class maintaining the results of the tests"""
1303 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001304
juraj.linkes184870a2018-07-16 14:22:01 +02001305 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001306 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001307 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001308 # ignore stream setting here, use hard-coded stdout to be in sync
1309 # with prints from VppTestCase methods ...
1310 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1311 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001312 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001313 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001314
juraj.linkesabec0122018-11-16 17:28:56 +01001315 self.orig_stream = self.stream
1316 self.resultclass.test_framework_result_pipe = result_pipe
1317
1318 self.print_summary = print_summary
1319
1320 def _makeResult(self):
1321 return self.resultclass(self.stream,
1322 self.descriptions,
1323 self.verbosity,
1324 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001325
Damjan Marionf56b77a2016-10-03 19:44:57 +02001326 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001327 """
1328 Run the tests
1329
1330 :param test:
1331
1332 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001333 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001334
1335 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001336 if not self.print_summary:
1337 self.stream = self.orig_stream
1338 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001339 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001340
1341
1342class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001343 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001344 self.logger = logger
1345 self.args = args
1346 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001347 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001348 super(Worker, self).__init__()
1349
1350 def run(self):
1351 executable = self.args[0]
1352 self.logger.debug("Running executable w/args `%s'" % self.args)
1353 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001354 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001355 env["CK_LOG_FILE_NAME"] = "-"
1356 self.process = subprocess.Popen(
1357 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1358 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1359 out, err = self.process.communicate()
1360 self.logger.debug("Finished running `%s'" % executable)
1361 self.logger.info("Return code is `%s'" % self.process.returncode)
1362 self.logger.info(single_line_delim)
1363 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1364 self.logger.info(single_line_delim)
1365 self.logger.info(out)
1366 self.logger.info(single_line_delim)
1367 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1368 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001369 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001370 self.logger.info(single_line_delim)
1371 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001372
1373if __name__ == '__main__':
1374 pass