blob: 5b29ac47cf4a1bfed626908db2564505e05435d9 [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 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800394 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100395 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100396 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100397 cls.logger = get_logger(cls.__name__)
398 if hasattr(cls, 'parallel_handler'):
399 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100400 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200401 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200402 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200403 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200404 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
405 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100406 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
407 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200408 cls.file_handler.setLevel(DEBUG)
409 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200410 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200411 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200412 cls.logger.info("Temporary dir is %s, shm prefix is %s",
413 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200414 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100415 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100416 cls._captures = []
417 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200418 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100419 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100420 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200421 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200422 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200423 # need to catch exceptions here because if we raise, then the cleanup
424 # doesn't get called and we might end with a zombie vpp
425 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200426 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200427 cls.reporter.send_keep_alive(cls, 'setUpClass')
428 VppTestResult.current_test_case_info = TestCaseInfo(
429 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100430 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100431 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100432 cls.pump_thread_stop_flag = Event()
433 cls.pump_thread_wakeup_pipe = os.pipe()
434 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100435 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100436 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200437 if cls.debug_gdb or cls.debug_gdbserver:
438 read_timeout = 0
439 else:
440 read_timeout = 5
441 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
442 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100443 if cls.step:
444 hook = StepHook(cls)
445 else:
446 hook = PollHook(cls)
447 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200448 cls.wait_for_stats_socket()
449 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200450 try:
451 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100452 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200453 cls.vpp_startup_failed = True
454 cls.logger.critical(
455 "VPP died shortly after startup, check the"
456 " output to standard error for possible cause")
457 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100458 try:
459 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100460 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100461 try:
462 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100463 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100464 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100465 if cls.debug_gdbserver:
466 print(colorize("You're running VPP inside gdbserver but "
467 "VPP-API connection failed, did you forget "
468 "to 'continue' VPP from within gdb?", RED))
469 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100470 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100471 try:
472 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100473 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100474 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100475 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200476
Damjan Marionf56b77a2016-10-03 19:44:57 +0200477 @classmethod
478 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200479 """
480 Disconnect vpp-api, kill vpp and cleanup shared memory files
481 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200482 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
483 cls.vpp.poll()
484 if cls.vpp.returncode is None:
485 print(double_line_delim)
486 print("VPP or GDB server is still running")
487 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800488 input("When done debugging, press ENTER to kill the "
489 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200490
juraj.linkes184870a2018-07-16 14:22:01 +0200491 # first signal that we want to stop the pump thread, then wake it up
492 if hasattr(cls, 'pump_thread_stop_flag'):
493 cls.pump_thread_stop_flag.set()
494 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100495 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100496 if hasattr(cls, 'pump_thread'):
497 cls.logger.debug("Waiting for pump thread to stop")
498 cls.pump_thread.join()
499 if hasattr(cls, 'vpp_stderr_reader_thread'):
500 cls.logger.debug("Waiting for stdderr pump to stop")
501 cls.vpp_stderr_reader_thread.join()
502
Klement Sekeraf62ae122016-10-11 11:47:09 +0200503 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100504 if hasattr(cls, 'vapi'):
505 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100506 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200507 cls.vpp.poll()
508 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100509 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200510 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100511 cls.logger.debug("Waiting for vpp to die")
512 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200513 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200514
Klement Sekera3747c752017-04-10 06:30:17 +0200515 if cls.vpp_startup_failed:
516 stdout_log = cls.logger.info
517 stderr_log = cls.logger.critical
518 else:
519 stdout_log = cls.logger.info
520 stderr_log = cls.logger.info
521
Klement Sekerae4504c62016-12-08 10:16:41 +0100522 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200523 stdout_log(single_line_delim)
524 stdout_log('VPP output to stdout while running %s:', cls.__name__)
525 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100526 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200527 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
528 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200529 stdout_log('\n%s', vpp_output)
530 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200531
Klement Sekerae4504c62016-12-08 10:16:41 +0100532 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200533 stderr_log(single_line_delim)
534 stderr_log('VPP output to stderr while running %s:', cls.__name__)
535 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100536 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200537 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
538 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200539 stderr_log('\n%s', vpp_output)
540 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200541
Damjan Marionf56b77a2016-10-03 19:44:57 +0200542 @classmethod
543 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200544 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200545 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200546 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200547 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100548 cls.reset_packet_infos()
549 if debug_framework:
550 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200551
Damjan Marionf56b77a2016-10-03 19:44:57 +0200552 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200553 """ Show various debug prints after each test """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800554 super(VppTestCase, self).tearDown()
Klement Sekerab91017a2017-02-09 06:04:36 +0100555 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
556 (self.__class__.__name__, self._testMethodName,
557 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200558 if not self.vpp_dead:
Klement Sekerad91fa612019-01-15 13:25:09 +0100559 self.logger.debug(self.vapi.cli("show trace max 1000"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700560 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200561 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200562 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200563 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800564 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100565 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500566 # Save/Dump VPP api trace log
567 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
568 tmp_api_trace = "/tmp/%s" % api_trace
569 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
570 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
571 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
572 vpp_api_trace_log))
573 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500574 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500575 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100576 else:
577 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200578
Damjan Marionf56b77a2016-10-03 19:44:57 +0200579 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200580 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800581 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200582 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100583 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
584 (self.__class__.__name__, self._testMethodName,
585 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100586 if self.vpp_dead:
587 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100588 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100589 self.vpp_stdout_deque.append(
590 "--- test setUp() for %s.%s(%s) starts here ---\n" %
591 (self.__class__.__name__, self._testMethodName,
592 self._testMethodDoc))
593 self.vpp_stderr_deque.append(
594 "--- test setUp() for %s.%s(%s) starts here ---\n" %
595 (self.__class__.__name__, self._testMethodName,
596 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200597 self.vapi.cli("clear trace")
598 # store the test instance inside the test class - so that objects
599 # holding the class can access instance methods (like assertEqual)
600 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200601
Damjan Marionf56b77a2016-10-03 19:44:57 +0200602 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200603 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200604 """
605 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200606
Klement Sekera75e7d132017-09-20 08:26:30 +0200607 :param interfaces: iterable interface indexes (if None,
608 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200609
Klement Sekeraf62ae122016-10-11 11:47:09 +0200610 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200611 if interfaces is None:
612 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200613 for i in interfaces:
614 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200615
Damjan Marionf56b77a2016-10-03 19:44:57 +0200616 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100617 def register_capture(cls, cap_name):
618 """ Register a capture in the testclass """
619 # add to the list of captures with current timestamp
620 cls._captures.append((time.time(), cap_name))
621 # filter out from zombies
622 cls._zombie_captures = [(stamp, name)
623 for (stamp, name) in cls._zombie_captures
624 if name != cap_name]
625
626 @classmethod
627 def pg_start(cls):
628 """ Remove any zombie captures and enable the packet generator """
629 # how long before capture is allowed to be deleted - otherwise vpp
630 # crashes - 100ms seems enough (this shouldn't be needed at all)
631 capture_ttl = 0.1
632 now = time.time()
633 for stamp, cap_name in cls._zombie_captures:
634 wait = stamp + capture_ttl - now
635 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100636 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100637 now = time.time()
638 cls.logger.debug("Removing zombie capture %s" % cap_name)
639 cls.vapi.cli('packet-generator delete %s' % cap_name)
640
Klement Sekerad91fa612019-01-15 13:25:09 +0100641 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200642 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100643 cls._zombie_captures = cls._captures
644 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200645
Damjan Marionf56b77a2016-10-03 19:44:57 +0200646 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200647 def create_pg_interfaces(cls, interfaces):
648 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100649 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200650
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100651 :param interfaces: iterable indexes of the interfaces.
652 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200653
Klement Sekeraf62ae122016-10-11 11:47:09 +0200654 """
655 result = []
656 for i in interfaces:
657 intf = VppPGInterface(cls, i)
658 setattr(cls, intf.name, intf)
659 result.append(intf)
660 cls.pg_interfaces = result
661 return result
662
Matej Klotton0178d522016-11-04 11:11:44 +0100663 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200664 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100665 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100666 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100667
Klement Sekerab9ef2732018-06-24 22:49:33 +0200668 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100669 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100670 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200671 result = [VppLoInterface(cls) for i in range(count)]
672 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100673 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100674 cls.lo_interfaces = result
675 return result
676
Damjan Marionf56b77a2016-10-03 19:44:57 +0200677 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200678 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200679 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200680 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200681 NOTE: Currently works only when Raw layer is present.
682
683 :param packet: packet
684 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200685 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200686
687 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200688 packet_len = len(packet) + 4
689 extend = size - packet_len
690 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200691 num = (extend / len(padding)) + 1
692 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200693
Klement Sekeradab231a2016-12-21 08:50:14 +0100694 @classmethod
695 def reset_packet_infos(cls):
696 """ Reset the list of packet info objects and packet counts to zero """
697 cls._packet_infos = {}
698 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200699
Klement Sekeradab231a2016-12-21 08:50:14 +0100700 @classmethod
701 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200702 """
703 Create packet info object containing the source and destination indexes
704 and add it to the testcase's packet info list
705
Klement Sekeradab231a2016-12-21 08:50:14 +0100706 :param VppInterface src_if: source interface
707 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200708
709 :returns: _PacketInfo object
710
711 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200712 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100713 info.index = len(cls._packet_infos)
714 info.src = src_if.sw_if_index
715 info.dst = dst_if.sw_if_index
716 if isinstance(dst_if, VppSubInterface):
717 dst_idx = dst_if.parent.sw_if_index
718 else:
719 dst_idx = dst_if.sw_if_index
720 if dst_idx in cls._packet_count_for_dst_if_idx:
721 cls._packet_count_for_dst_if_idx[dst_idx] += 1
722 else:
723 cls._packet_count_for_dst_if_idx[dst_idx] = 1
724 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200725 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200726
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727 @staticmethod
728 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200729 """
730 Convert _PacketInfo object to packet payload
731
732 :param info: _PacketInfo object
733
734 :returns: string containing serialized data from packet info
735 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100736 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
737 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200738
Damjan Marionf56b77a2016-10-03 19:44:57 +0200739 @staticmethod
740 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200741 """
742 Convert packet payload to _PacketInfo object
743
744 :param payload: packet payload
745
746 :returns: _PacketInfo object containing de-serialized data from payload
747
748 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200749 numbers = payload.split()
750 info = _PacketInfo()
751 info.index = int(numbers[0])
752 info.src = int(numbers[1])
753 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100754 info.ip = int(numbers[3])
755 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200756 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200757
Damjan Marionf56b77a2016-10-03 19:44:57 +0200758 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200759 """
760 Iterate over the packet info list stored in the testcase
761 Start iteration with first element if info is None
762 Continue based on index in info if info is specified
763
764 :param info: info or None
765 :returns: next info in list or None if no more infos
766 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200767 if info is None:
768 next_index = 0
769 else:
770 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100771 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200772 return None
773 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100774 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200775
Klement Sekeraf62ae122016-10-11 11:47:09 +0200776 def get_next_packet_info_for_interface(self, src_index, info):
777 """
778 Search the packet info list for the next packet info with same source
779 interface index
780
781 :param src_index: source interface index to search for
782 :param info: packet info - where to start the search
783 :returns: packet info or None
784
785 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200786 while True:
787 info = self.get_next_packet_info(info)
788 if info is None:
789 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200790 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200791 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200792
Klement Sekeraf62ae122016-10-11 11:47:09 +0200793 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
794 """
795 Search the packet info list for the next packet info with same source
796 and destination interface indexes
797
798 :param src_index: source interface index to search for
799 :param dst_index: destination interface index to search for
800 :param info: packet info - where to start the search
801 :returns: packet info or None
802
803 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200804 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200805 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200806 if info is None:
807 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200808 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200809 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200810
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200811 def assert_equal(self, real_value, expected_value, name_or_class=None):
812 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100813 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200814 return
815 try:
816 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
817 msg = msg % (getdoc(name_or_class).strip(),
818 real_value, str(name_or_class(real_value)),
819 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100820 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200821 msg = "Invalid %s: %s does not match expected value %s" % (
822 name_or_class, real_value, expected_value)
823
824 self.assertEqual(real_value, expected_value, msg)
825
Klement Sekerab17dd962017-01-09 07:43:48 +0100826 def assert_in_range(self,
827 real_value,
828 expected_min,
829 expected_max,
830 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200831 if name is None:
832 msg = None
833 else:
834 msg = "Invalid %s: %s out of range <%s,%s>" % (
835 name, real_value, expected_min, expected_max)
836 self.assertTrue(expected_min <= real_value <= expected_max, msg)
837
Klement Sekerad81ae412018-05-16 10:52:54 +0200838 def assert_packet_checksums_valid(self, packet,
839 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200840 received = packet.__class__(str(packet))
841 self.logger.debug(
842 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200843 udp_layers = ['UDP', 'UDPerror']
844 checksum_fields = ['cksum', 'chksum']
845 checksums = []
846 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200847 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200848 while True:
849 layer = temp.getlayer(counter)
850 if layer:
851 for cf in checksum_fields:
852 if hasattr(layer, cf):
853 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100854 0 == getattr(layer, cf) and \
855 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200856 continue
857 delattr(layer, cf)
858 checksums.append((counter, cf))
859 else:
860 break
861 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200862 if 0 == len(checksums):
863 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200864 temp = temp.__class__(str(temp))
865 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200866 calc_sum = getattr(temp[layer], cf)
867 self.assert_equal(
868 getattr(received[layer], cf), calc_sum,
869 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
870 self.logger.debug(
871 "Checksum field `%s` on `%s` layer has correct value `%s`" %
872 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200873
874 def assert_checksum_valid(self, received_packet, layer,
875 field_name='chksum',
876 ignore_zero_checksum=False):
877 """ Check checksum of received packet on given layer """
878 received_packet_checksum = getattr(received_packet[layer], field_name)
879 if ignore_zero_checksum and 0 == received_packet_checksum:
880 return
881 recalculated = received_packet.__class__(str(received_packet))
882 delattr(recalculated[layer], field_name)
883 recalculated = recalculated.__class__(str(recalculated))
884 self.assert_equal(received_packet_checksum,
885 getattr(recalculated[layer], field_name),
886 "packet checksum on layer: %s" % layer)
887
888 def assert_ip_checksum_valid(self, received_packet,
889 ignore_zero_checksum=False):
890 self.assert_checksum_valid(received_packet, 'IP',
891 ignore_zero_checksum=ignore_zero_checksum)
892
893 def assert_tcp_checksum_valid(self, received_packet,
894 ignore_zero_checksum=False):
895 self.assert_checksum_valid(received_packet, 'TCP',
896 ignore_zero_checksum=ignore_zero_checksum)
897
898 def assert_udp_checksum_valid(self, received_packet,
899 ignore_zero_checksum=True):
900 self.assert_checksum_valid(received_packet, 'UDP',
901 ignore_zero_checksum=ignore_zero_checksum)
902
903 def assert_embedded_icmp_checksum_valid(self, received_packet):
904 if received_packet.haslayer(IPerror):
905 self.assert_checksum_valid(received_packet, 'IPerror')
906 if received_packet.haslayer(TCPerror):
907 self.assert_checksum_valid(received_packet, 'TCPerror')
908 if received_packet.haslayer(UDPerror):
909 self.assert_checksum_valid(received_packet, 'UDPerror',
910 ignore_zero_checksum=True)
911 if received_packet.haslayer(ICMPerror):
912 self.assert_checksum_valid(received_packet, 'ICMPerror')
913
914 def assert_icmp_checksum_valid(self, received_packet):
915 self.assert_checksum_valid(received_packet, 'ICMP')
916 self.assert_embedded_icmp_checksum_valid(received_packet)
917
918 def assert_icmpv6_checksum_valid(self, pkt):
919 if pkt.haslayer(ICMPv6DestUnreach):
920 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
921 self.assert_embedded_icmp_checksum_valid(pkt)
922 if pkt.haslayer(ICMPv6EchoRequest):
923 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
924 if pkt.haslayer(ICMPv6EchoReply):
925 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
926
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100927 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +0100928 if counter.startswith("/"):
929 counter_value = self.statistics.get_counter(counter)
930 self.assert_equal(counter_value, expected_value,
931 "packet counter `%s'" % counter)
932 else:
933 counters = self.vapi.cli("sh errors").split('\n')
934 counter_value = -1
935 for i in range(1, len(counters) - 1):
936 results = counters[i].split()
937 if results[1] == counter:
938 counter_value = int(results[0])
939 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100940
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100941 @classmethod
942 def sleep(cls, timeout, remark=None):
943 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800944 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000945 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100946 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000947 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800948 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200949 cls.logger.error("unexpected time.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800950 "slept for %es instead of ~%es!",
951 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000952 if hasattr(cls, 'logger'):
953 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800954 "Finished sleep (%s) - slept %es (wanted %es)",
955 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +0100956
Neale Ranns947ea622018-06-07 23:48:20 -0700957 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800958 self.vapi.cli("clear trace")
959 intf.add_stream(pkts)
960 self.pg_enable_capture(self.pg_interfaces)
961 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700962 if not timeout:
963 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800964 for i in self.pg_interfaces:
965 i.get_capture(0, timeout=timeout)
966 i.assert_nothing_captured(remark=remark)
967 timeout = 0.1
968
969 def send_and_expect(self, input, pkts, output):
970 self.vapi.cli("clear trace")
971 input.add_stream(pkts)
972 self.pg_enable_capture(self.pg_interfaces)
973 self.pg_start()
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -0800974 rx = output.get_capture(len(pkts))
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700975 return rx
976
977 def send_and_expect_only(self, input, pkts, output, timeout=None):
978 self.vapi.cli("clear trace")
979 input.add_stream(pkts)
980 self.pg_enable_capture(self.pg_interfaces)
981 self.pg_start()
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -0800982 rx = output.get_capture(len(pkts))
983 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700984 if not timeout:
985 timeout = 1
986 for i in self.pg_interfaces:
987 if i not in outputs:
988 i.get_capture(0, timeout=timeout)
989 i.assert_nothing_captured()
990 timeout = 0.1
991
Neale Ranns52fae862018-01-08 04:41:42 -0800992 return rx
993
Paul Vinciguerra087c8112018-12-15 08:03:09 -0800994 def runTest(self):
995 """ unittest calls runTest when TestCase is instantiated without a
996 test case. Use case: Writing unittests against VppTestCase"""
997 pass
998
Damjan Marionf56b77a2016-10-03 19:44:57 +0200999
juraj.linkes184870a2018-07-16 14:22:01 +02001000def get_testcase_doc_name(test):
1001 return getdoc(test.__class__).splitlines()[0]
1002
1003
Ole Trøan5ba91592018-11-22 10:01:09 +00001004def get_test_description(descriptions, test):
1005 short_description = test.shortDescription()
1006 if descriptions and short_description:
1007 return short_description
1008 else:
1009 return str(test)
1010
1011
juraj.linkes40dd73b2018-09-21 13:55:16 +02001012class TestCaseInfo(object):
1013 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1014 self.logger = logger
1015 self.tempdir = tempdir
1016 self.vpp_pid = vpp_pid
1017 self.vpp_bin_path = vpp_bin_path
1018 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001019
1020
Damjan Marionf56b77a2016-10-03 19:44:57 +02001021class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001022 """
1023 @property result_string
1024 String variable to store the test case result string.
1025 @property errors
1026 List variable containing 2-tuples of TestCase instances and strings
1027 holding formatted tracebacks. Each tuple represents a test which
1028 raised an unexpected exception.
1029 @property failures
1030 List variable containing 2-tuples of TestCase instances and strings
1031 holding formatted tracebacks. Each tuple represents a test where
1032 a failure was explicitly signalled using the TestCase.assert*()
1033 methods.
1034 """
1035
juraj.linkes40dd73b2018-09-21 13:55:16 +02001036 failed_test_cases_info = set()
1037 core_crash_test_cases_info = set()
1038 current_test_case_info = None
1039
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001040 def __init__(self, stream=None, descriptions=None, verbosity=None,
1041 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001042 """
Klement Sekerada505f62017-01-04 12:58:53 +01001043 :param stream File descriptor to store where to report test results.
1044 Set to the standard error stream by default.
1045 :param descriptions Boolean variable to store information if to use
1046 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001047 :param verbosity Integer variable to store required verbosity level.
1048 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001049 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001050 self.stream = stream
1051 self.descriptions = descriptions
1052 self.verbosity = verbosity
1053 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001054 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001055
Damjan Marionf56b77a2016-10-03 19:44:57 +02001056 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001057 """
1058 Record a test succeeded result
1059
1060 :param test:
1061
1062 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001063 if self.current_test_case_info:
1064 self.current_test_case_info.logger.debug(
1065 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1066 test._testMethodName,
1067 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001068 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001069 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001070
juraj.linkescae64f82018-09-19 15:01:47 +02001071 self.send_result_through_pipe(test, PASS)
1072
Klement Sekeraf62ae122016-10-11 11:47:09 +02001073 def addSkip(self, test, reason):
1074 """
1075 Record a test skipped.
1076
1077 :param test:
1078 :param reason:
1079
1080 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001081 if self.current_test_case_info:
1082 self.current_test_case_info.logger.debug(
1083 "--- addSkip() %s.%s(%s) called, reason is %s" %
1084 (test.__class__.__name__, test._testMethodName,
1085 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001086 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001087 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001088
juraj.linkescae64f82018-09-19 15:01:47 +02001089 self.send_result_through_pipe(test, SKIP)
1090
juraj.linkes40dd73b2018-09-21 13:55:16 +02001091 def symlink_failed(self):
1092 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001093 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001094 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001095 link_path = os.path.join(
1096 failed_dir,
1097 '%s-FAILED' %
1098 os.path.basename(self.current_test_case_info.tempdir))
1099 if self.current_test_case_info.logger:
1100 self.current_test_case_info.logger.debug(
1101 "creating a link to the failed test")
1102 self.current_test_case_info.logger.debug(
1103 "os.symlink(%s, %s)" %
1104 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001105 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001106 if self.current_test_case_info.logger:
1107 self.current_test_case_info.logger.debug(
1108 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001109 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001110 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001111
Klement Sekeraf413bef2017-08-15 07:09:02 +02001112 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001113 if self.current_test_case_info.logger:
1114 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001115
juraj.linkescae64f82018-09-19 15:01:47 +02001116 def send_result_through_pipe(self, test, result):
1117 if hasattr(self, 'test_framework_result_pipe'):
1118 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001119 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001120 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001121
juraj.linkes40dd73b2018-09-21 13:55:16 +02001122 def log_error(self, test, err, fn_name):
1123 if self.current_test_case_info:
1124 if isinstance(test, unittest.suite._ErrorHolder):
1125 test_name = test.description
1126 else:
1127 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1128 test._testMethodName,
1129 test._testMethodDoc)
1130 self.current_test_case_info.logger.debug(
1131 "--- %s() %s called, err is %s" %
1132 (fn_name, test_name, err))
1133 self.current_test_case_info.logger.debug(
1134 "formatted exception is:\n%s" %
1135 "".join(format_exception(*err)))
1136
1137 def add_error(self, test, err, unittest_fn, error_type):
1138 if error_type == FAIL:
1139 self.log_error(test, err, 'addFailure')
1140 error_type_str = colorize("FAIL", RED)
1141 elif error_type == ERROR:
1142 self.log_error(test, err, 'addError')
1143 error_type_str = colorize("ERROR", RED)
1144 else:
1145 raise Exception('Error type %s cannot be used to record an '
1146 'error or a failure' % error_type)
1147
1148 unittest_fn(self, test, err)
1149 if self.current_test_case_info:
1150 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1151 (error_type_str,
1152 self.current_test_case_info.tempdir)
1153 self.symlink_failed()
1154 self.failed_test_cases_info.add(self.current_test_case_info)
1155 if is_core_present(self.current_test_case_info.tempdir):
1156 if not self.current_test_case_info.core_crash_test:
1157 if isinstance(test, unittest.suite._ErrorHolder):
1158 test_name = str(test)
1159 else:
1160 test_name = "'{}' ({})".format(
1161 get_testcase_doc_name(test), test.id())
1162 self.current_test_case_info.core_crash_test = test_name
1163 self.core_crash_test_cases_info.add(
1164 self.current_test_case_info)
1165 else:
1166 self.result_string = '%s [no temp dir]' % error_type_str
1167
1168 self.send_result_through_pipe(test, error_type)
1169
Damjan Marionf56b77a2016-10-03 19:44:57 +02001170 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001171 """
1172 Record a test failed result
1173
1174 :param test:
1175 :param err: error message
1176
1177 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001178 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001179
Damjan Marionf56b77a2016-10-03 19:44:57 +02001180 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001181 """
1182 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001183
Klement Sekeraf62ae122016-10-11 11:47:09 +02001184 :param test:
1185 :param err: error message
1186
1187 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001188 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001189
Damjan Marionf56b77a2016-10-03 19:44:57 +02001190 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001191 """
1192 Get test description
1193
1194 :param test:
1195 :returns: test description
1196
1197 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001198 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001199
Damjan Marionf56b77a2016-10-03 19:44:57 +02001200 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001201 """
1202 Start a test
1203
1204 :param test:
1205
1206 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001207
1208 def print_header(test):
1209 if not hasattr(test.__class__, '_header_printed'):
1210 print(double_line_delim)
1211 print(colorize(getdoc(test).splitlines()[0], GREEN))
1212 print(double_line_delim)
1213 test.__class__._header_printed = True
1214
1215 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001216
Damjan Marionf56b77a2016-10-03 19:44:57 +02001217 unittest.TestResult.startTest(self, test)
1218 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001219 self.stream.writeln(
1220 "Starting " + self.getDescription(test) + " ...")
1221 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001222
Damjan Marionf56b77a2016-10-03 19:44:57 +02001223 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001224 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001225 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001226
1227 :param test:
1228
1229 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001230 unittest.TestResult.stopTest(self, test)
1231 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001232 self.stream.writeln(single_line_delim)
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))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001235 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001236 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001237 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001238 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001239
1240 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001241
Damjan Marionf56b77a2016-10-03 19:44:57 +02001242 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001243 """
1244 Print errors from running the test case
1245 """
juraj.linkesabec0122018-11-16 17:28:56 +01001246 if len(self.errors) > 0 or len(self.failures) > 0:
1247 self.stream.writeln()
1248 self.printErrorList('ERROR', self.errors)
1249 self.printErrorList('FAIL', self.failures)
1250
1251 # ^^ that is the last output from unittest before summary
1252 if not self.runner.print_summary:
1253 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1254 self.stream = devnull
1255 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001256
Damjan Marionf56b77a2016-10-03 19:44:57 +02001257 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001258 """
1259 Print error list to the output stream together with error type
1260 and test case description.
1261
1262 :param flavour: error type
1263 :param errors: iterable errors
1264
1265 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001266 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001267 self.stream.writeln(double_line_delim)
1268 self.stream.writeln("%s: %s" %
1269 (flavour, self.getDescription(test)))
1270 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001271 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001272
1273
Damjan Marionf56b77a2016-10-03 19:44:57 +02001274class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001275 """
Klement Sekera104543f2017-02-03 07:29:43 +01001276 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001277 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001278
Klement Sekeraf62ae122016-10-11 11:47:09 +02001279 @property
1280 def resultclass(self):
1281 """Class maintaining the results of the tests"""
1282 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001283
juraj.linkes184870a2018-07-16 14:22:01 +02001284 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001285 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001286 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001287 # ignore stream setting here, use hard-coded stdout to be in sync
1288 # with prints from VppTestCase methods ...
1289 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1290 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001291 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001292 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001293
juraj.linkesabec0122018-11-16 17:28:56 +01001294 self.orig_stream = self.stream
1295 self.resultclass.test_framework_result_pipe = result_pipe
1296
1297 self.print_summary = print_summary
1298
1299 def _makeResult(self):
1300 return self.resultclass(self.stream,
1301 self.descriptions,
1302 self.verbosity,
1303 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001304
Damjan Marionf56b77a2016-10-03 19:44:57 +02001305 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001306 """
1307 Run the tests
1308
1309 :param test:
1310
1311 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001312 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001313
1314 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001315 if not self.print_summary:
1316 self.stream = self.orig_stream
1317 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001318 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001319
1320
1321class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001322 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001323 self.logger = logger
1324 self.args = args
1325 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001326 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001327 super(Worker, self).__init__()
1328
1329 def run(self):
1330 executable = self.args[0]
1331 self.logger.debug("Running executable w/args `%s'" % self.args)
1332 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001333 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001334 env["CK_LOG_FILE_NAME"] = "-"
1335 self.process = subprocess.Popen(
1336 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1337 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1338 out, err = self.process.communicate()
1339 self.logger.debug("Finished running `%s'" % executable)
1340 self.logger.info("Return code is `%s'" % self.process.returncode)
1341 self.logger.info(single_line_delim)
1342 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1343 self.logger.info(single_line_delim)
1344 self.logger.info(out)
1345 self.logger.info(single_line_delim)
1346 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1347 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001348 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001349 self.logger.info(single_line_delim)
1350 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001351
1352if __name__ == '__main__':
1353 pass