blob: ea637eba9ebd7e580e1da06425d9667aea07dc31 [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:
Klement Sekera931be3a2016-11-03 05:36:01 +0100370 cls.logger.critical("Couldn't start vpp: %s" % e)
371 raise
372
Klement Sekera277b89c2016-10-28 13:20:27 +0200373 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100374
Damjan Marionf56b77a2016-10-03 19:44:57 +0200375 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200376 def wait_for_stats_socket(cls):
377 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800378 ok = False
379 while time.time() < deadline or \
380 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200381 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800382 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200383 break
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800384 time.sleep(0.8)
385 if not ok:
386 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200387
388 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200389 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200390 """
391 Perform class setup before running the testcase
392 Remove shared memory files, start vpp and connect the vpp-api
393 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100394 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100395 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100396 cls.logger = get_logger(cls.__name__)
397 if hasattr(cls, 'parallel_handler'):
398 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100399 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200400 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200401 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200402 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200403 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
404 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100405 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
406 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200407 cls.file_handler.setLevel(DEBUG)
408 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200409 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200410 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200411 cls.logger.info("Temporary dir is %s, shm prefix is %s",
412 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200413 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100414 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100415 cls._captures = []
416 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200417 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100418 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100419 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200420 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200421 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200422 # need to catch exceptions here because if we raise, then the cleanup
423 # doesn't get called and we might end with a zombie vpp
424 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200425 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200426 cls.reporter.send_keep_alive(cls, 'setUpClass')
427 VppTestResult.current_test_case_info = TestCaseInfo(
428 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100429 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100430 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100431 cls.pump_thread_stop_flag = Event()
432 cls.pump_thread_wakeup_pipe = os.pipe()
433 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100434 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100435 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200436 if cls.debug_gdb or cls.debug_gdbserver:
437 read_timeout = 0
438 else:
439 read_timeout = 5
440 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
441 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100442 if cls.step:
443 hook = StepHook(cls)
444 else:
445 hook = PollHook(cls)
446 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200447 cls.wait_for_stats_socket()
448 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200449 try:
450 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100451 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200452 cls.vpp_startup_failed = True
453 cls.logger.critical(
454 "VPP died shortly after startup, check the"
455 " output to standard error for possible cause")
456 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100457 try:
458 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100459 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100460 try:
461 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100462 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100463 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100464 if cls.debug_gdbserver:
465 print(colorize("You're running VPP inside gdbserver but "
466 "VPP-API connection failed, did you forget "
467 "to 'continue' VPP from within gdb?", RED))
468 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100469 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100470 try:
471 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100472 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100473 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100474 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200475
Damjan Marionf56b77a2016-10-03 19:44:57 +0200476 @classmethod
477 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200478 """
479 Disconnect vpp-api, kill vpp and cleanup shared memory files
480 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200481 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
482 cls.vpp.poll()
483 if cls.vpp.returncode is None:
484 print(double_line_delim)
485 print("VPP or GDB server is still running")
486 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800487 input("When done debugging, press ENTER to kill the "
488 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200489
juraj.linkes184870a2018-07-16 14:22:01 +0200490 # first signal that we want to stop the pump thread, then wake it up
491 if hasattr(cls, 'pump_thread_stop_flag'):
492 cls.pump_thread_stop_flag.set()
493 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100494 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100495 if hasattr(cls, 'pump_thread'):
496 cls.logger.debug("Waiting for pump thread to stop")
497 cls.pump_thread.join()
498 if hasattr(cls, 'vpp_stderr_reader_thread'):
499 cls.logger.debug("Waiting for stdderr pump to stop")
500 cls.vpp_stderr_reader_thread.join()
501
Klement Sekeraf62ae122016-10-11 11:47:09 +0200502 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100503 if hasattr(cls, 'vapi'):
504 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100505 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200506 cls.vpp.poll()
507 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100508 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200509 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100510 cls.logger.debug("Waiting for vpp to die")
511 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200512 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200513
Klement Sekera3747c752017-04-10 06:30:17 +0200514 if cls.vpp_startup_failed:
515 stdout_log = cls.logger.info
516 stderr_log = cls.logger.critical
517 else:
518 stdout_log = cls.logger.info
519 stderr_log = cls.logger.info
520
Klement Sekerae4504c62016-12-08 10:16:41 +0100521 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200522 stdout_log(single_line_delim)
523 stdout_log('VPP output to stdout while running %s:', cls.__name__)
524 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100525 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200526 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
527 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200528 stdout_log('\n%s', vpp_output)
529 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200530
Klement Sekerae4504c62016-12-08 10:16:41 +0100531 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200532 stderr_log(single_line_delim)
533 stderr_log('VPP output to stderr while running %s:', cls.__name__)
534 stderr_log(single_line_delim)
Ole Troan7f991832018-12-06 17:35:12 +0100535 vpp_output = "".join(str(cls.vpp_stderr_deque))
Klement Sekera027dbd52017-04-11 06:01:53 +0200536 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
537 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200538 stderr_log('\n%s', vpp_output)
539 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200540
Damjan Marionf56b77a2016-10-03 19:44:57 +0200541 @classmethod
542 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200543 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200544 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200545 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200546 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100547 cls.reset_packet_infos()
548 if debug_framework:
549 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200550
Damjan Marionf56b77a2016-10-03 19:44:57 +0200551 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200552 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100553 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
554 (self.__class__.__name__, self._testMethodName,
555 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200556 if not self.vpp_dead:
Klement Sekerad91fa612019-01-15 13:25:09 +0100557 self.logger.debug(self.vapi.cli("show trace max 1000"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700558 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200559 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200560 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200561 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800562 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100563 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500564 # Save/Dump VPP api trace log
565 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
566 tmp_api_trace = "/tmp/%s" % api_trace
567 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
568 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
569 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
570 vpp_api_trace_log))
571 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500572 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500573 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100574 else:
575 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200576
Damjan Marionf56b77a2016-10-03 19:44:57 +0200577 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200578 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200579 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100580 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
581 (self.__class__.__name__, self._testMethodName,
582 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100583 if self.vpp_dead:
584 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100585 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100586 self.vpp_stdout_deque.append(
587 "--- test setUp() for %s.%s(%s) starts here ---\n" %
588 (self.__class__.__name__, self._testMethodName,
589 self._testMethodDoc))
590 self.vpp_stderr_deque.append(
591 "--- test setUp() for %s.%s(%s) starts here ---\n" %
592 (self.__class__.__name__, self._testMethodName,
593 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200594 self.vapi.cli("clear trace")
595 # store the test instance inside the test class - so that objects
596 # holding the class can access instance methods (like assertEqual)
597 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200598
Damjan Marionf56b77a2016-10-03 19:44:57 +0200599 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200600 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200601 """
602 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200603
Klement Sekera75e7d132017-09-20 08:26:30 +0200604 :param interfaces: iterable interface indexes (if None,
605 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200606
Klement Sekeraf62ae122016-10-11 11:47:09 +0200607 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200608 if interfaces is None:
609 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200610 for i in interfaces:
611 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200612
Damjan Marionf56b77a2016-10-03 19:44:57 +0200613 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100614 def register_capture(cls, cap_name):
615 """ Register a capture in the testclass """
616 # add to the list of captures with current timestamp
617 cls._captures.append((time.time(), cap_name))
618 # filter out from zombies
619 cls._zombie_captures = [(stamp, name)
620 for (stamp, name) in cls._zombie_captures
621 if name != cap_name]
622
623 @classmethod
624 def pg_start(cls):
625 """ Remove any zombie captures and enable the packet generator """
626 # how long before capture is allowed to be deleted - otherwise vpp
627 # crashes - 100ms seems enough (this shouldn't be needed at all)
628 capture_ttl = 0.1
629 now = time.time()
630 for stamp, cap_name in cls._zombie_captures:
631 wait = stamp + capture_ttl - now
632 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100633 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100634 now = time.time()
635 cls.logger.debug("Removing zombie capture %s" % cap_name)
636 cls.vapi.cli('packet-generator delete %s' % cap_name)
637
Klement Sekerad91fa612019-01-15 13:25:09 +0100638 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200639 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100640 cls._zombie_captures = cls._captures
641 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200642
Damjan Marionf56b77a2016-10-03 19:44:57 +0200643 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200644 def create_pg_interfaces(cls, interfaces):
645 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100646 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200647
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100648 :param interfaces: iterable indexes of the interfaces.
649 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200650
Klement Sekeraf62ae122016-10-11 11:47:09 +0200651 """
652 result = []
653 for i in interfaces:
654 intf = VppPGInterface(cls, i)
655 setattr(cls, intf.name, intf)
656 result.append(intf)
657 cls.pg_interfaces = result
658 return result
659
Matej Klotton0178d522016-11-04 11:11:44 +0100660 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200661 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100662 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100663 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100664
Klement Sekerab9ef2732018-06-24 22:49:33 +0200665 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100666 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100667 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200668 result = [VppLoInterface(cls) for i in range(count)]
669 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100670 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100671 cls.lo_interfaces = result
672 return result
673
Damjan Marionf56b77a2016-10-03 19:44:57 +0200674 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200675 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200676 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200677 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200678 NOTE: Currently works only when Raw layer is present.
679
680 :param packet: packet
681 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200682 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200683
684 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200685 packet_len = len(packet) + 4
686 extend = size - packet_len
687 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200688 num = (extend / len(padding)) + 1
689 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200690
Klement Sekeradab231a2016-12-21 08:50:14 +0100691 @classmethod
692 def reset_packet_infos(cls):
693 """ Reset the list of packet info objects and packet counts to zero """
694 cls._packet_infos = {}
695 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200696
Klement Sekeradab231a2016-12-21 08:50:14 +0100697 @classmethod
698 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200699 """
700 Create packet info object containing the source and destination indexes
701 and add it to the testcase's packet info list
702
Klement Sekeradab231a2016-12-21 08:50:14 +0100703 :param VppInterface src_if: source interface
704 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200705
706 :returns: _PacketInfo object
707
708 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200709 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100710 info.index = len(cls._packet_infos)
711 info.src = src_if.sw_if_index
712 info.dst = dst_if.sw_if_index
713 if isinstance(dst_if, VppSubInterface):
714 dst_idx = dst_if.parent.sw_if_index
715 else:
716 dst_idx = dst_if.sw_if_index
717 if dst_idx in cls._packet_count_for_dst_if_idx:
718 cls._packet_count_for_dst_if_idx[dst_idx] += 1
719 else:
720 cls._packet_count_for_dst_if_idx[dst_idx] = 1
721 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200722 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200723
Damjan Marionf56b77a2016-10-03 19:44:57 +0200724 @staticmethod
725 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200726 """
727 Convert _PacketInfo object to packet payload
728
729 :param info: _PacketInfo object
730
731 :returns: string containing serialized data from packet info
732 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100733 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
734 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200735
Damjan Marionf56b77a2016-10-03 19:44:57 +0200736 @staticmethod
737 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200738 """
739 Convert packet payload to _PacketInfo object
740
741 :param payload: packet payload
742
743 :returns: _PacketInfo object containing de-serialized data from payload
744
745 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200746 numbers = payload.split()
747 info = _PacketInfo()
748 info.index = int(numbers[0])
749 info.src = int(numbers[1])
750 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100751 info.ip = int(numbers[3])
752 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200753 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200754
Damjan Marionf56b77a2016-10-03 19:44:57 +0200755 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200756 """
757 Iterate over the packet info list stored in the testcase
758 Start iteration with first element if info is None
759 Continue based on index in info if info is specified
760
761 :param info: info or None
762 :returns: next info in list or None if no more infos
763 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200764 if info is None:
765 next_index = 0
766 else:
767 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100768 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769 return None
770 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100771 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200772
Klement Sekeraf62ae122016-10-11 11:47:09 +0200773 def get_next_packet_info_for_interface(self, src_index, info):
774 """
775 Search the packet info list for the next packet info with same source
776 interface index
777
778 :param src_index: source interface index to search for
779 :param info: packet info - where to start the search
780 :returns: packet info or None
781
782 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200783 while True:
784 info = self.get_next_packet_info(info)
785 if info is None:
786 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200787 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200788 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200789
Klement Sekeraf62ae122016-10-11 11:47:09 +0200790 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
791 """
792 Search the packet info list for the next packet info with same source
793 and destination interface indexes
794
795 :param src_index: source interface index to search for
796 :param dst_index: destination interface index to search for
797 :param info: packet info - where to start the search
798 :returns: packet info or None
799
800 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200801 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200802 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200803 if info is None:
804 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200805 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200806 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200807
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200808 def assert_equal(self, real_value, expected_value, name_or_class=None):
809 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100810 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200811 return
812 try:
813 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
814 msg = msg % (getdoc(name_or_class).strip(),
815 real_value, str(name_or_class(real_value)),
816 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100817 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200818 msg = "Invalid %s: %s does not match expected value %s" % (
819 name_or_class, real_value, expected_value)
820
821 self.assertEqual(real_value, expected_value, msg)
822
Klement Sekerab17dd962017-01-09 07:43:48 +0100823 def assert_in_range(self,
824 real_value,
825 expected_min,
826 expected_max,
827 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200828 if name is None:
829 msg = None
830 else:
831 msg = "Invalid %s: %s out of range <%s,%s>" % (
832 name, real_value, expected_min, expected_max)
833 self.assertTrue(expected_min <= real_value <= expected_max, msg)
834
Klement Sekerad81ae412018-05-16 10:52:54 +0200835 def assert_packet_checksums_valid(self, packet,
836 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200837 received = packet.__class__(str(packet))
838 self.logger.debug(
839 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200840 udp_layers = ['UDP', 'UDPerror']
841 checksum_fields = ['cksum', 'chksum']
842 checksums = []
843 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200844 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200845 while True:
846 layer = temp.getlayer(counter)
847 if layer:
848 for cf in checksum_fields:
849 if hasattr(layer, cf):
850 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100851 0 == getattr(layer, cf) and \
852 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200853 continue
854 delattr(layer, cf)
855 checksums.append((counter, cf))
856 else:
857 break
858 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200859 if 0 == len(checksums):
860 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200861 temp = temp.__class__(str(temp))
862 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200863 calc_sum = getattr(temp[layer], cf)
864 self.assert_equal(
865 getattr(received[layer], cf), calc_sum,
866 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
867 self.logger.debug(
868 "Checksum field `%s` on `%s` layer has correct value `%s`" %
869 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200870
871 def assert_checksum_valid(self, received_packet, layer,
872 field_name='chksum',
873 ignore_zero_checksum=False):
874 """ Check checksum of received packet on given layer """
875 received_packet_checksum = getattr(received_packet[layer], field_name)
876 if ignore_zero_checksum and 0 == received_packet_checksum:
877 return
878 recalculated = received_packet.__class__(str(received_packet))
879 delattr(recalculated[layer], field_name)
880 recalculated = recalculated.__class__(str(recalculated))
881 self.assert_equal(received_packet_checksum,
882 getattr(recalculated[layer], field_name),
883 "packet checksum on layer: %s" % layer)
884
885 def assert_ip_checksum_valid(self, received_packet,
886 ignore_zero_checksum=False):
887 self.assert_checksum_valid(received_packet, 'IP',
888 ignore_zero_checksum=ignore_zero_checksum)
889
890 def assert_tcp_checksum_valid(self, received_packet,
891 ignore_zero_checksum=False):
892 self.assert_checksum_valid(received_packet, 'TCP',
893 ignore_zero_checksum=ignore_zero_checksum)
894
895 def assert_udp_checksum_valid(self, received_packet,
896 ignore_zero_checksum=True):
897 self.assert_checksum_valid(received_packet, 'UDP',
898 ignore_zero_checksum=ignore_zero_checksum)
899
900 def assert_embedded_icmp_checksum_valid(self, received_packet):
901 if received_packet.haslayer(IPerror):
902 self.assert_checksum_valid(received_packet, 'IPerror')
903 if received_packet.haslayer(TCPerror):
904 self.assert_checksum_valid(received_packet, 'TCPerror')
905 if received_packet.haslayer(UDPerror):
906 self.assert_checksum_valid(received_packet, 'UDPerror',
907 ignore_zero_checksum=True)
908 if received_packet.haslayer(ICMPerror):
909 self.assert_checksum_valid(received_packet, 'ICMPerror')
910
911 def assert_icmp_checksum_valid(self, received_packet):
912 self.assert_checksum_valid(received_packet, 'ICMP')
913 self.assert_embedded_icmp_checksum_valid(received_packet)
914
915 def assert_icmpv6_checksum_valid(self, pkt):
916 if pkt.haslayer(ICMPv6DestUnreach):
917 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
918 self.assert_embedded_icmp_checksum_valid(pkt)
919 if pkt.haslayer(ICMPv6EchoRequest):
920 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
921 if pkt.haslayer(ICMPv6EchoReply):
922 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
923
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100924 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +0100925 if counter.startswith("/"):
926 counter_value = self.statistics.get_counter(counter)
927 self.assert_equal(counter_value, expected_value,
928 "packet counter `%s'" % counter)
929 else:
930 counters = self.vapi.cli("sh errors").split('\n')
931 counter_value = -1
932 for i in range(1, len(counters) - 1):
933 results = counters[i].split()
934 if results[1] == counter:
935 counter_value = int(results[0])
936 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100937
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100938 @classmethod
939 def sleep(cls, timeout, remark=None):
940 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800941 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000942 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100943 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000944 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800945 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200946 cls.logger.error("unexpected time.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800947 "slept for %es instead of ~%es!",
948 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000949 if hasattr(cls, 'logger'):
950 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800951 "Finished sleep (%s) - slept %es (wanted %es)",
952 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +0100953
Neale Ranns947ea622018-06-07 23:48:20 -0700954 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800955 self.vapi.cli("clear trace")
956 intf.add_stream(pkts)
957 self.pg_enable_capture(self.pg_interfaces)
958 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700959 if not timeout:
960 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800961 for i in self.pg_interfaces:
962 i.get_capture(0, timeout=timeout)
963 i.assert_nothing_captured(remark=remark)
964 timeout = 0.1
965
966 def send_and_expect(self, input, pkts, output):
967 self.vapi.cli("clear trace")
968 input.add_stream(pkts)
969 self.pg_enable_capture(self.pg_interfaces)
970 self.pg_start()
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -0800971 rx = output.get_capture(len(pkts))
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700972 return rx
973
974 def send_and_expect_only(self, input, pkts, output, timeout=None):
975 self.vapi.cli("clear trace")
976 input.add_stream(pkts)
977 self.pg_enable_capture(self.pg_interfaces)
978 self.pg_start()
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -0800979 rx = output.get_capture(len(pkts))
980 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700981 if not timeout:
982 timeout = 1
983 for i in self.pg_interfaces:
984 if i not in outputs:
985 i.get_capture(0, timeout=timeout)
986 i.assert_nothing_captured()
987 timeout = 0.1
988
Neale Ranns52fae862018-01-08 04:41:42 -0800989 return rx
990
Paul Vinciguerra087c8112018-12-15 08:03:09 -0800991 def runTest(self):
992 """ unittest calls runTest when TestCase is instantiated without a
993 test case. Use case: Writing unittests against VppTestCase"""
994 pass
995
Damjan Marionf56b77a2016-10-03 19:44:57 +0200996
juraj.linkes184870a2018-07-16 14:22:01 +0200997def get_testcase_doc_name(test):
998 return getdoc(test.__class__).splitlines()[0]
999
1000
Ole Trøan5ba91592018-11-22 10:01:09 +00001001def get_test_description(descriptions, test):
1002 short_description = test.shortDescription()
1003 if descriptions and short_description:
1004 return short_description
1005 else:
1006 return str(test)
1007
1008
juraj.linkes40dd73b2018-09-21 13:55:16 +02001009class TestCaseInfo(object):
1010 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1011 self.logger = logger
1012 self.tempdir = tempdir
1013 self.vpp_pid = vpp_pid
1014 self.vpp_bin_path = vpp_bin_path
1015 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001016
1017
Damjan Marionf56b77a2016-10-03 19:44:57 +02001018class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001019 """
1020 @property result_string
1021 String variable to store the test case result string.
1022 @property errors
1023 List variable containing 2-tuples of TestCase instances and strings
1024 holding formatted tracebacks. Each tuple represents a test which
1025 raised an unexpected exception.
1026 @property failures
1027 List variable containing 2-tuples of TestCase instances and strings
1028 holding formatted tracebacks. Each tuple represents a test where
1029 a failure was explicitly signalled using the TestCase.assert*()
1030 methods.
1031 """
1032
juraj.linkes40dd73b2018-09-21 13:55:16 +02001033 failed_test_cases_info = set()
1034 core_crash_test_cases_info = set()
1035 current_test_case_info = None
1036
juraj.linkesabec0122018-11-16 17:28:56 +01001037 def __init__(self, stream, descriptions, verbosity, runner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001038 """
Klement Sekerada505f62017-01-04 12:58:53 +01001039 :param stream File descriptor to store where to report test results.
1040 Set to the standard error stream by default.
1041 :param descriptions Boolean variable to store information if to use
1042 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001043 :param verbosity Integer variable to store required verbosity level.
1044 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001045 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001046 self.stream = stream
1047 self.descriptions = descriptions
1048 self.verbosity = verbosity
1049 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001050 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001051
Damjan Marionf56b77a2016-10-03 19:44:57 +02001052 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001053 """
1054 Record a test succeeded result
1055
1056 :param test:
1057
1058 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001059 if self.current_test_case_info:
1060 self.current_test_case_info.logger.debug(
1061 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1062 test._testMethodName,
1063 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001064 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001065 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001066
juraj.linkescae64f82018-09-19 15:01:47 +02001067 self.send_result_through_pipe(test, PASS)
1068
Klement Sekeraf62ae122016-10-11 11:47:09 +02001069 def addSkip(self, test, reason):
1070 """
1071 Record a test skipped.
1072
1073 :param test:
1074 :param reason:
1075
1076 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001077 if self.current_test_case_info:
1078 self.current_test_case_info.logger.debug(
1079 "--- addSkip() %s.%s(%s) called, reason is %s" %
1080 (test.__class__.__name__, test._testMethodName,
1081 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001082 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001083 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001084
juraj.linkescae64f82018-09-19 15:01:47 +02001085 self.send_result_through_pipe(test, SKIP)
1086
juraj.linkes40dd73b2018-09-21 13:55:16 +02001087 def symlink_failed(self):
1088 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001089 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001090 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001091 link_path = os.path.join(
1092 failed_dir,
1093 '%s-FAILED' %
1094 os.path.basename(self.current_test_case_info.tempdir))
1095 if self.current_test_case_info.logger:
1096 self.current_test_case_info.logger.debug(
1097 "creating a link to the failed test")
1098 self.current_test_case_info.logger.debug(
1099 "os.symlink(%s, %s)" %
1100 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001101 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001102 if self.current_test_case_info.logger:
1103 self.current_test_case_info.logger.debug(
1104 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001105 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001106 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001107
Klement Sekeraf413bef2017-08-15 07:09:02 +02001108 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001109 if self.current_test_case_info.logger:
1110 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001111
juraj.linkescae64f82018-09-19 15:01:47 +02001112 def send_result_through_pipe(self, test, result):
1113 if hasattr(self, 'test_framework_result_pipe'):
1114 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001115 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001116 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001117
juraj.linkes40dd73b2018-09-21 13:55:16 +02001118 def log_error(self, test, err, fn_name):
1119 if self.current_test_case_info:
1120 if isinstance(test, unittest.suite._ErrorHolder):
1121 test_name = test.description
1122 else:
1123 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1124 test._testMethodName,
1125 test._testMethodDoc)
1126 self.current_test_case_info.logger.debug(
1127 "--- %s() %s called, err is %s" %
1128 (fn_name, test_name, err))
1129 self.current_test_case_info.logger.debug(
1130 "formatted exception is:\n%s" %
1131 "".join(format_exception(*err)))
1132
1133 def add_error(self, test, err, unittest_fn, error_type):
1134 if error_type == FAIL:
1135 self.log_error(test, err, 'addFailure')
1136 error_type_str = colorize("FAIL", RED)
1137 elif error_type == ERROR:
1138 self.log_error(test, err, 'addError')
1139 error_type_str = colorize("ERROR", RED)
1140 else:
1141 raise Exception('Error type %s cannot be used to record an '
1142 'error or a failure' % error_type)
1143
1144 unittest_fn(self, test, err)
1145 if self.current_test_case_info:
1146 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1147 (error_type_str,
1148 self.current_test_case_info.tempdir)
1149 self.symlink_failed()
1150 self.failed_test_cases_info.add(self.current_test_case_info)
1151 if is_core_present(self.current_test_case_info.tempdir):
1152 if not self.current_test_case_info.core_crash_test:
1153 if isinstance(test, unittest.suite._ErrorHolder):
1154 test_name = str(test)
1155 else:
1156 test_name = "'{}' ({})".format(
1157 get_testcase_doc_name(test), test.id())
1158 self.current_test_case_info.core_crash_test = test_name
1159 self.core_crash_test_cases_info.add(
1160 self.current_test_case_info)
1161 else:
1162 self.result_string = '%s [no temp dir]' % error_type_str
1163
1164 self.send_result_through_pipe(test, error_type)
1165
Damjan Marionf56b77a2016-10-03 19:44:57 +02001166 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001167 """
1168 Record a test failed result
1169
1170 :param test:
1171 :param err: error message
1172
1173 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001174 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001175
Damjan Marionf56b77a2016-10-03 19:44:57 +02001176 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001177 """
1178 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001179
Klement Sekeraf62ae122016-10-11 11:47:09 +02001180 :param test:
1181 :param err: error message
1182
1183 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001184 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001185
Damjan Marionf56b77a2016-10-03 19:44:57 +02001186 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001187 """
1188 Get test description
1189
1190 :param test:
1191 :returns: test description
1192
1193 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001194 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001195
Damjan Marionf56b77a2016-10-03 19:44:57 +02001196 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001197 """
1198 Start a test
1199
1200 :param test:
1201
1202 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001203
1204 def print_header(test):
1205 if not hasattr(test.__class__, '_header_printed'):
1206 print(double_line_delim)
1207 print(colorize(getdoc(test).splitlines()[0], GREEN))
1208 print(double_line_delim)
1209 test.__class__._header_printed = True
1210
1211 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001212
Damjan Marionf56b77a2016-10-03 19:44:57 +02001213 unittest.TestResult.startTest(self, test)
1214 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001215 self.stream.writeln(
1216 "Starting " + self.getDescription(test) + " ...")
1217 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001218
Damjan Marionf56b77a2016-10-03 19:44:57 +02001219 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001220 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001221 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001222
1223 :param test:
1224
1225 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001226 unittest.TestResult.stopTest(self, test)
1227 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001228 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001229 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001230 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001231 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001232 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001233 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001234 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001235
1236 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001237
Damjan Marionf56b77a2016-10-03 19:44:57 +02001238 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001239 """
1240 Print errors from running the test case
1241 """
juraj.linkesabec0122018-11-16 17:28:56 +01001242 if len(self.errors) > 0 or len(self.failures) > 0:
1243 self.stream.writeln()
1244 self.printErrorList('ERROR', self.errors)
1245 self.printErrorList('FAIL', self.failures)
1246
1247 # ^^ that is the last output from unittest before summary
1248 if not self.runner.print_summary:
1249 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1250 self.stream = devnull
1251 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001252
Damjan Marionf56b77a2016-10-03 19:44:57 +02001253 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001254 """
1255 Print error list to the output stream together with error type
1256 and test case description.
1257
1258 :param flavour: error type
1259 :param errors: iterable errors
1260
1261 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001262 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001263 self.stream.writeln(double_line_delim)
1264 self.stream.writeln("%s: %s" %
1265 (flavour, self.getDescription(test)))
1266 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001267 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001268
1269
Damjan Marionf56b77a2016-10-03 19:44:57 +02001270class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001271 """
Klement Sekera104543f2017-02-03 07:29:43 +01001272 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001273 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001274
Klement Sekeraf62ae122016-10-11 11:47:09 +02001275 @property
1276 def resultclass(self):
1277 """Class maintaining the results of the tests"""
1278 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001279
juraj.linkes184870a2018-07-16 14:22:01 +02001280 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001281 result_pipe=None, failfast=False, buffer=False,
juraj.linkesabec0122018-11-16 17:28:56 +01001282 resultclass=None, print_summary=True):
Klement Sekera7a161da2017-01-17 13:42:48 +01001283 # ignore stream setting here, use hard-coded stdout to be in sync
1284 # with prints from VppTestCase methods ...
1285 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1286 verbosity, failfast, buffer,
1287 resultclass)
juraj.linkesccfead62018-11-21 13:20:43 +01001288 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001289
juraj.linkesabec0122018-11-16 17:28:56 +01001290 self.orig_stream = self.stream
1291 self.resultclass.test_framework_result_pipe = result_pipe
1292
1293 self.print_summary = print_summary
1294
1295 def _makeResult(self):
1296 return self.resultclass(self.stream,
1297 self.descriptions,
1298 self.verbosity,
1299 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001300
Damjan Marionf56b77a2016-10-03 19:44:57 +02001301 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001302 """
1303 Run the tests
1304
1305 :param test:
1306
1307 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001308 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001309
1310 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001311 if not self.print_summary:
1312 self.stream = self.orig_stream
1313 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001314 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001315
1316
1317class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001318 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001319 self.logger = logger
1320 self.args = args
1321 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001322 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001323 super(Worker, self).__init__()
1324
1325 def run(self):
1326 executable = self.args[0]
1327 self.logger.debug("Running executable w/args `%s'" % self.args)
1328 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001329 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001330 env["CK_LOG_FILE_NAME"] = "-"
1331 self.process = subprocess.Popen(
1332 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1333 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1334 out, err = self.process.communicate()
1335 self.logger.debug("Finished running `%s'" % executable)
1336 self.logger.info("Return code is `%s'" % self.process.returncode)
1337 self.logger.info(single_line_delim)
1338 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1339 self.logger.info(single_line_delim)
1340 self.logger.info(out)
1341 self.logger.info(single_line_delim)
1342 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1343 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001344 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001345 self.logger.info(single_line_delim)
1346 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001347
1348if __name__ == '__main__':
1349 pass