blob: ea989eb242db8e3cfabf88502e068f9283a3b8b9 [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
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070021
22import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000023from scapy.packet import Raw
24from hook import StepHook, PollHook, VppDiedError
Paul Vinciguerra919efad2018-12-17 21:43:43 -080025from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010026from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000027from vpp_lo_interface import VppLoInterface
28from vpp_papi_provider import VppPapiProvider
29from vpp_papi.vpp_stats import VPPStats
30from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
31 get_logger, colorize
32from vpp_object import VppObjectRegistry
33from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020034from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
35from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
36from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080037
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010038if os.name == 'posix' and sys.version_info[0] < 3:
39 # using subprocess32 is recommended by python official documentation
40 # @ https://docs.python.org/2/library/subprocess.html
41 import subprocess32 as subprocess
42else:
43 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020044
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080045# Python2/3 compatible
46try:
47 input = raw_input
48except NameError:
49 pass
50
juraj.linkescae64f82018-09-19 15:01:47 +020051PASS = 0
52FAIL = 1
53ERROR = 2
54SKIP = 3
55TEST_RUN = 4
56
Klement Sekeraebbaf552018-02-17 13:41:33 +010057debug_framework = False
58if os.getenv('TEST_DEBUG', "0") == "1":
59 debug_framework = True
60 import debug_internal
61
Klement Sekeraf62ae122016-10-11 11:47:09 +020062"""
63 Test framework module.
64
65 The module provides a set of tools for constructing and running tests and
66 representing the results.
67"""
68
Klement Sekeraf62ae122016-10-11 11:47:09 +020069
Damjan Marionf56b77a2016-10-03 19:44:57 +020070class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020071 """Private class to create packet info object.
72
73 Help process information about the next packet.
74 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020075 """
Matej Klotton86d87c42016-11-11 11:38:55 +010076 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020077 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010078 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020079 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010080 #: Store the index of the destination packet generator interface
81 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020082 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010083 #: Store expected ip version
84 ip = -1
85 #: Store expected upper protocol
86 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010087 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020088 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020089
Matej Klotton16a14cd2016-12-07 15:09:13 +010090 def __eq__(self, other):
91 index = self.index == other.index
92 src = self.src == other.src
93 dst = self.dst == other.dst
94 data = self.data == other.data
95 return index and src and dst and data
96
Klement Sekeraf62ae122016-10-11 11:47:09 +020097
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010098def pump_output(testclass):
99 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100100 stdout_fragment = ""
101 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400102 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100103 readable = select.select([testclass.vpp.stdout.fileno(),
104 testclass.vpp.stderr.fileno(),
105 testclass.pump_thread_wakeup_pipe[0]],
106 [], [])[0]
107 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100108 read = os.read(testclass.vpp.stdout.fileno(), 102400)
109 if len(read) > 0:
110 split = read.splitlines(True)
111 if len(stdout_fragment) > 0:
112 split[0] = "%s%s" % (stdout_fragment, split[0])
113 if len(split) > 0 and split[-1].endswith("\n"):
114 limit = None
115 else:
116 limit = -1
117 stdout_fragment = split[-1]
118 testclass.vpp_stdout_deque.extend(split[:limit])
119 if not testclass.cache_vpp_output:
120 for line in split[:limit]:
121 testclass.logger.debug(
122 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100123 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100124 read = os.read(testclass.vpp.stderr.fileno(), 102400)
125 if len(read) > 0:
126 split = read.splitlines(True)
127 if len(stderr_fragment) > 0:
128 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan7f991832018-12-06 17:35:12 +0100129 if len(split) > 0 and split[-1].endswith(b"\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100130 limit = None
131 else:
132 limit = -1
133 stderr_fragment = split[-1]
134 testclass.vpp_stderr_deque.extend(split[:limit])
135 if not testclass.cache_vpp_output:
136 for line in split[:limit]:
137 testclass.logger.debug(
138 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800139 # ignoring the dummy pipe here intentionally - the
140 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200141
142
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800143def _is_skip_aarch64_set():
juraj.linkes68ebc832018-11-29 09:37:08 +0100144 return os.getenv('SKIP_AARCH64', 'n').lower() in ('yes', 'y', '1')
145
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800146is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100147
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800148
149def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100150 return platform.machine() == 'aarch64'
151
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800152is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100153
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800154
155def _running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100156 s = os.getenv("EXTENDED_TESTS", "n")
157 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100158
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800159running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100160
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800161
162def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100163 os_id = os.getenv("OS_ID", "")
164 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200165
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800166running_on_centos = _running_on_centos
167
Klement Sekerad3e671e2017-09-29 12:36:37 +0200168
Klement Sekera909a6a12017-08-08 04:33:53 +0200169class KeepAliveReporter(object):
170 """
171 Singleton object which reports test start to parent process
172 """
173 _shared_state = {}
174
175 def __init__(self):
176 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800177 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200178
179 @property
180 def pipe(self):
181 return self._pipe
182
183 @pipe.setter
184 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800185 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200186 raise Exception("Internal error - pipe should only be set once.")
187 self._pipe = pipe
188
juraj.linkes40dd73b2018-09-21 13:55:16 +0200189 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200190 """
191 Write current test tmpdir & desc to keep-alive pipe to signal liveness
192 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200193 if self.pipe is None:
194 # if not running forked..
195 return
196
Klement Sekera909a6a12017-08-08 04:33:53 +0200197 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200198 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200199 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200200 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200201
Dave Wallacee2efd122017-09-30 22:04:21 -0400202 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200203
204
Damjan Marionf56b77a2016-10-03 19:44:57 +0200205class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100206 """This subclass is a base class for VPP test cases that are implemented as
207 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200208 """
209
Ole Troana45dc072018-12-21 16:04:22 +0100210 extra_vpp_punt_config = []
211 extra_vpp_plugin_config = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100212
Klement Sekeraf62ae122016-10-11 11:47:09 +0200213 @property
214 def packet_infos(self):
215 """List of packet infos"""
216 return self._packet_infos
217
Klement Sekeradab231a2016-12-21 08:50:14 +0100218 @classmethod
219 def get_packet_count_for_if_idx(cls, dst_if_index):
220 """Get the number of packet info for specified destination if index"""
221 if dst_if_index in cls._packet_count_for_dst_if_idx:
222 return cls._packet_count_for_dst_if_idx[dst_if_index]
223 else:
224 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200225
226 @classmethod
227 def instance(cls):
228 """Return the instance of this testcase"""
229 return cls.test_instance
230
Damjan Marionf56b77a2016-10-03 19:44:57 +0200231 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200232 def set_debug_flags(cls, d):
233 cls.debug_core = False
234 cls.debug_gdb = False
235 cls.debug_gdbserver = False
236 if d is None:
237 return
238 dl = d.lower()
239 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200240 cls.debug_core = True
241 elif dl == "gdb":
242 cls.debug_gdb = True
243 elif dl == "gdbserver":
244 cls.debug_gdbserver = True
245 else:
246 raise Exception("Unrecognized DEBUG option: '%s'" % d)
247
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800248 @staticmethod
249 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200250 cpu_usage_list = [set(range(psutil.cpu_count()))]
251 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
252 if 'vpp_main' == p.info['name']]
253 for vpp_process in vpp_processes:
254 for cpu_usage_set in cpu_usage_list:
255 try:
256 cpu_num = vpp_process.cpu_num()
257 if cpu_num in cpu_usage_set:
258 cpu_usage_set_index = cpu_usage_list.index(
259 cpu_usage_set)
260 if cpu_usage_set_index == len(cpu_usage_list) - 1:
261 cpu_usage_list.append({cpu_num})
262 else:
263 cpu_usage_list[cpu_usage_set_index + 1].add(
264 cpu_num)
265 cpu_usage_set.remove(cpu_num)
266 break
267 except psutil.NoSuchProcess:
268 pass
269
270 for cpu_usage_set in cpu_usage_list:
271 if len(cpu_usage_set) > 0:
272 min_usage_set = cpu_usage_set
273 break
274
275 return random.choice(tuple(min_usage_set))
276
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800277 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200278 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200279 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100280 s = os.getenv("STEP", "n")
281 cls.step = True if s.lower() in ("y", "yes", "1") else False
282 d = os.getenv("DEBUG", None)
283 c = os.getenv("CACHE_OUTPUT", "1")
284 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200285 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100286 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
287 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100288 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
289 plugin_path = None
290 if cls.plugin_path is not None:
291 if cls.extern_plugin_path is not None:
292 plugin_path = "%s:%s" % (
293 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100294 else:
295 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100296 elif cls.extern_plugin_path is not None:
297 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100298 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100299 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100300 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100301 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100302 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100303 if size is not None:
304 coredump_size = "coredump-size %s" % size
305 if coredump_size is None:
306 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200307
Ole Troana45dc072018-12-21 16:04:22 +0100308 cpu_core_number = cls.get_least_used_cpu()
juraj.linkes184870a2018-07-16 14:22:01 +0200309
Ole Troana45dc072018-12-21 16:04:22 +0100310 cls.vpp_cmdline = [cls.vpp_bin, "unix",
311 "{", "nodaemon", debug_cli, "full-coredump",
312 coredump_size, "runtime-dir", cls.tempdir, "}",
313 "api-trace", "{", "on", "}", "api-segment", "{",
314 "prefix", cls.shm_prefix, "}", "cpu", "{",
315 "main-core", str(cpu_core_number), "}", "statseg",
316 "{", "socket-name", cls.stats_sock, "}", "plugins",
317 "{", "plugin", "dpdk_plugin.so", "{", "disable",
318 "}", "plugin", "unittest_plugin.so", "{", "enable",
319 "}"] + cls.extra_vpp_plugin_config + ["}", ]
320 if cls.extra_vpp_punt_config is not None:
321 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100322 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100323 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100324 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
325 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200326
327 @classmethod
328 def wait_for_enter(cls):
329 if cls.debug_gdbserver:
330 print(double_line_delim)
331 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
332 elif cls.debug_gdb:
333 print(double_line_delim)
334 print("Spawned VPP with PID: %d" % cls.vpp.pid)
335 else:
336 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
337 return
338 print(single_line_delim)
339 print("You can debug the VPP using e.g.:")
340 if cls.debug_gdbserver:
341 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
342 print("Now is the time to attach a gdb by running the above "
343 "command, set up breakpoints etc. and then resume VPP from "
344 "within gdb by issuing the 'continue' command")
345 elif cls.debug_gdb:
346 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
347 print("Now is the time to attach a gdb by running the above "
348 "command and set up breakpoints etc.")
349 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800350 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200351
352 @classmethod
353 def run_vpp(cls):
354 cmdline = cls.vpp_cmdline
355
356 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100357 gdbserver = '/usr/bin/gdbserver'
358 if not os.path.isfile(gdbserver) or \
359 not os.access(gdbserver, os.X_OK):
360 raise Exception("gdbserver binary '%s' does not exist or is "
361 "not executable" % gdbserver)
362
363 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200364 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
365
Klement Sekera931be3a2016-11-03 05:36:01 +0100366 try:
367 cls.vpp = subprocess.Popen(cmdline,
368 stdout=subprocess.PIPE,
369 stderr=subprocess.PIPE,
370 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800371 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800372 cls.logger.critical("Subprocess returned with non-0 return code: ("
373 "%s)", e.returncode)
374 raise
375 except OSError as e:
376 cls.logger.critical("Subprocess returned with OS error: "
377 "(%s) %s", e.errno, e.strerror)
378 raise
379 except Exception as e:
380 cls.logger.exception("Subprocess returned unexpected from "
381 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100382 raise
383
Klement Sekera277b89c2016-10-28 13:20:27 +0200384 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100385
Damjan Marionf56b77a2016-10-03 19:44:57 +0200386 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200387 def wait_for_stats_socket(cls):
388 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800389 ok = False
390 while time.time() < deadline or \
391 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200392 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800393 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200394 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700395 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800396 if not ok:
397 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200398
399 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200400 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200401 """
402 Perform class setup before running the testcase
403 Remove shared memory files, start vpp and connect the vpp-api
404 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800405 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100406 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100407 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100408 cls.logger = get_logger(cls.__name__)
409 if hasattr(cls, 'parallel_handler'):
410 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100411 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200412 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200413 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200414 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200415 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
416 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100417 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
418 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200419 cls.file_handler.setLevel(DEBUG)
420 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200421 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200422 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200423 cls.logger.info("Temporary dir is %s, shm prefix is %s",
424 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200425 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100426 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100427 cls._captures = []
428 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200429 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100430 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100431 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200432 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200433 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200434 # need to catch exceptions here because if we raise, then the cleanup
435 # doesn't get called and we might end with a zombie vpp
436 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200437 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200438 cls.reporter.send_keep_alive(cls, 'setUpClass')
439 VppTestResult.current_test_case_info = TestCaseInfo(
440 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100441 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100442 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100443 cls.pump_thread_stop_flag = Event()
444 cls.pump_thread_wakeup_pipe = os.pipe()
445 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100446 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100447 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200448 if cls.debug_gdb or cls.debug_gdbserver:
449 read_timeout = 0
450 else:
451 read_timeout = 5
452 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
453 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100454 if cls.step:
455 hook = StepHook(cls)
456 else:
457 hook = PollHook(cls)
458 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200459 cls.wait_for_stats_socket()
460 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200461 try:
462 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100463 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200464 cls.vpp_startup_failed = True
465 cls.logger.critical(
466 "VPP died shortly after startup, check the"
467 " output to standard error for possible cause")
468 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100469 try:
470 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100471 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100472 try:
473 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100474 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100475 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100476 if cls.debug_gdbserver:
477 print(colorize("You're running VPP inside gdbserver but "
478 "VPP-API connection failed, did you forget "
479 "to 'continue' VPP from within gdb?", RED))
480 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100481 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100482 try:
483 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100484 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100485 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100486 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200487
Damjan Marionf56b77a2016-10-03 19:44:57 +0200488 @classmethod
489 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200490 """
491 Disconnect vpp-api, kill vpp and cleanup shared memory files
492 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200493 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
494 cls.vpp.poll()
495 if cls.vpp.returncode is None:
496 print(double_line_delim)
497 print("VPP or GDB server is still running")
498 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800499 input("When done debugging, press ENTER to kill the "
500 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200501
juraj.linkes184870a2018-07-16 14:22:01 +0200502 # first signal that we want to stop the pump thread, then wake it up
503 if hasattr(cls, 'pump_thread_stop_flag'):
504 cls.pump_thread_stop_flag.set()
505 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100506 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100507 if hasattr(cls, 'pump_thread'):
508 cls.logger.debug("Waiting for pump thread to stop")
509 cls.pump_thread.join()
510 if hasattr(cls, 'vpp_stderr_reader_thread'):
511 cls.logger.debug("Waiting for stdderr pump to stop")
512 cls.vpp_stderr_reader_thread.join()
513
Klement Sekeraf62ae122016-10-11 11:47:09 +0200514 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100515 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700516 cls.logger.debug("Disconnecting class vapi client on %s",
517 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100518 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700519 cls.logger.debug("Deleting class vapi attribute on %s",
520 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100521 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200522 cls.vpp.poll()
523 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100524 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200525 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100526 cls.logger.debug("Waiting for vpp to die")
527 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700528 cls.logger.debug("Deleting class vpp attribute on %s",
529 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200530 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200531
Klement Sekera3747c752017-04-10 06:30:17 +0200532 if cls.vpp_startup_failed:
533 stdout_log = cls.logger.info
534 stderr_log = cls.logger.critical
535 else:
536 stdout_log = cls.logger.info
537 stderr_log = cls.logger.info
538
Klement Sekerae4504c62016-12-08 10:16:41 +0100539 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200540 stdout_log(single_line_delim)
541 stdout_log('VPP output to stdout while running %s:', cls.__name__)
542 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100543 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200544 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
545 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200546 stdout_log('\n%s', vpp_output)
547 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200548
Klement Sekerae4504c62016-12-08 10:16:41 +0100549 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200550 stderr_log(single_line_delim)
551 stderr_log('VPP output to stderr while running %s:', cls.__name__)
552 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100553 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200554 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
555 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200556 stderr_log('\n%s', vpp_output)
557 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200558
Damjan Marionf56b77a2016-10-03 19:44:57 +0200559 @classmethod
560 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200561 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200562 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200563 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200564 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100565 cls.reset_packet_infos()
566 if debug_framework:
567 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200568
Damjan Marionf56b77a2016-10-03 19:44:57 +0200569 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200570 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100571 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
572 (self.__class__.__name__, self._testMethodName,
573 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200574 if not self.vpp_dead:
Klement Sekerad91fa612019-01-15 13:25:09 +0100575 self.logger.debug(self.vapi.cli("show trace max 1000"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700576 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200577 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200578 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200579 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800580 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100581 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500582 # Save/Dump VPP api trace log
583 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
584 tmp_api_trace = "/tmp/%s" % api_trace
585 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
586 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
587 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
588 vpp_api_trace_log))
589 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500590 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500591 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100592 else:
593 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200594
Damjan Marionf56b77a2016-10-03 19:44:57 +0200595 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200596 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800597 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200598 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100599 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
600 (self.__class__.__name__, self._testMethodName,
601 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100602 if self.vpp_dead:
603 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100604 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100605 self.vpp_stdout_deque.append(
606 "--- test setUp() for %s.%s(%s) starts here ---\n" %
607 (self.__class__.__name__, self._testMethodName,
608 self._testMethodDoc))
609 self.vpp_stderr_deque.append(
610 "--- test setUp() for %s.%s(%s) starts here ---\n" %
611 (self.__class__.__name__, self._testMethodName,
612 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200613 self.vapi.cli("clear trace")
614 # store the test instance inside the test class - so that objects
615 # holding the class can access instance methods (like assertEqual)
616 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200617
Damjan Marionf56b77a2016-10-03 19:44:57 +0200618 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200619 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200620 """
621 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200622
Klement Sekera75e7d132017-09-20 08:26:30 +0200623 :param interfaces: iterable interface indexes (if None,
624 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200625
Klement Sekeraf62ae122016-10-11 11:47:09 +0200626 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200627 if interfaces is None:
628 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200629 for i in interfaces:
630 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200631
Damjan Marionf56b77a2016-10-03 19:44:57 +0200632 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100633 def register_capture(cls, cap_name):
634 """ Register a capture in the testclass """
635 # add to the list of captures with current timestamp
636 cls._captures.append((time.time(), cap_name))
637 # filter out from zombies
638 cls._zombie_captures = [(stamp, name)
639 for (stamp, name) in cls._zombie_captures
640 if name != cap_name]
641
642 @classmethod
643 def pg_start(cls):
644 """ Remove any zombie captures and enable the packet generator """
645 # how long before capture is allowed to be deleted - otherwise vpp
646 # crashes - 100ms seems enough (this shouldn't be needed at all)
647 capture_ttl = 0.1
648 now = time.time()
649 for stamp, cap_name in cls._zombie_captures:
650 wait = stamp + capture_ttl - now
651 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100652 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100653 now = time.time()
654 cls.logger.debug("Removing zombie capture %s" % cap_name)
655 cls.vapi.cli('packet-generator delete %s' % cap_name)
656
Klement Sekerad91fa612019-01-15 13:25:09 +0100657 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200658 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100659 cls._zombie_captures = cls._captures
660 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200661
Damjan Marionf56b77a2016-10-03 19:44:57 +0200662 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200663 def create_pg_interfaces(cls, interfaces):
664 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100665 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200666
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100667 :param interfaces: iterable indexes of the interfaces.
668 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200669
Klement Sekeraf62ae122016-10-11 11:47:09 +0200670 """
671 result = []
672 for i in interfaces:
673 intf = VppPGInterface(cls, i)
674 setattr(cls, intf.name, intf)
675 result.append(intf)
676 cls.pg_interfaces = result
677 return result
678
Matej Klotton0178d522016-11-04 11:11:44 +0100679 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200680 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100681 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100682 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100683
Klement Sekerab9ef2732018-06-24 22:49:33 +0200684 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100685 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100686 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200687 result = [VppLoInterface(cls) for i in range(count)]
688 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100689 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100690 cls.lo_interfaces = result
691 return result
692
Damjan Marionf56b77a2016-10-03 19:44:57 +0200693 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200694 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200695 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200696 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200697 NOTE: Currently works only when Raw layer is present.
698
699 :param packet: packet
700 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200701 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200702
703 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200704 packet_len = len(packet) + 4
705 extend = size - packet_len
706 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200707 num = (extend / len(padding)) + 1
708 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200709
Klement Sekeradab231a2016-12-21 08:50:14 +0100710 @classmethod
711 def reset_packet_infos(cls):
712 """ Reset the list of packet info objects and packet counts to zero """
713 cls._packet_infos = {}
714 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200715
Klement Sekeradab231a2016-12-21 08:50:14 +0100716 @classmethod
717 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200718 """
719 Create packet info object containing the source and destination indexes
720 and add it to the testcase's packet info list
721
Klement Sekeradab231a2016-12-21 08:50:14 +0100722 :param VppInterface src_if: source interface
723 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200724
725 :returns: _PacketInfo object
726
727 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200728 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100729 info.index = len(cls._packet_infos)
730 info.src = src_if.sw_if_index
731 info.dst = dst_if.sw_if_index
732 if isinstance(dst_if, VppSubInterface):
733 dst_idx = dst_if.parent.sw_if_index
734 else:
735 dst_idx = dst_if.sw_if_index
736 if dst_idx in cls._packet_count_for_dst_if_idx:
737 cls._packet_count_for_dst_if_idx[dst_idx] += 1
738 else:
739 cls._packet_count_for_dst_if_idx[dst_idx] = 1
740 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200741 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200742
Damjan Marionf56b77a2016-10-03 19:44:57 +0200743 @staticmethod
744 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200745 """
746 Convert _PacketInfo object to packet payload
747
748 :param info: _PacketInfo object
749
750 :returns: string containing serialized data from packet info
751 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100752 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
753 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200754
Damjan Marionf56b77a2016-10-03 19:44:57 +0200755 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800756 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200757 """
758 Convert packet payload to _PacketInfo object
759
760 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700761 :type payload: <class 'scapy.packet.Raw'>
762 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800763 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700764 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200765 :returns: _PacketInfo object containing de-serialized data from payload
766
767 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800768 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769 info = _PacketInfo()
770 info.index = int(numbers[0])
771 info.src = int(numbers[1])
772 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100773 info.ip = int(numbers[3])
774 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200775 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200776
Damjan Marionf56b77a2016-10-03 19:44:57 +0200777 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200778 """
779 Iterate over the packet info list stored in the testcase
780 Start iteration with first element if info is None
781 Continue based on index in info if info is specified
782
783 :param info: info or None
784 :returns: next info in list or None if no more infos
785 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200786 if info is None:
787 next_index = 0
788 else:
789 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100790 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200791 return None
792 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100793 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200794
Klement Sekeraf62ae122016-10-11 11:47:09 +0200795 def get_next_packet_info_for_interface(self, src_index, info):
796 """
797 Search the packet info list for the next packet info with same source
798 interface index
799
800 :param src_index: source interface index to search for
801 :param info: packet info - where to start the search
802 :returns: packet info or None
803
804 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200805 while True:
806 info = self.get_next_packet_info(info)
807 if info is None:
808 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200809 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200810 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200811
Klement Sekeraf62ae122016-10-11 11:47:09 +0200812 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
813 """
814 Search the packet info list for the next packet info with same source
815 and destination interface indexes
816
817 :param src_index: source interface index to search for
818 :param dst_index: destination interface index to search for
819 :param info: packet info - where to start the search
820 :returns: packet info or None
821
822 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200823 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200824 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200825 if info is None:
826 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200827 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200828 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200829
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200830 def assert_equal(self, real_value, expected_value, name_or_class=None):
831 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100832 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200833 return
834 try:
835 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
836 msg = msg % (getdoc(name_or_class).strip(),
837 real_value, str(name_or_class(real_value)),
838 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100839 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200840 msg = "Invalid %s: %s does not match expected value %s" % (
841 name_or_class, real_value, expected_value)
842
843 self.assertEqual(real_value, expected_value, msg)
844
Klement Sekerab17dd962017-01-09 07:43:48 +0100845 def assert_in_range(self,
846 real_value,
847 expected_min,
848 expected_max,
849 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200850 if name is None:
851 msg = None
852 else:
853 msg = "Invalid %s: %s out of range <%s,%s>" % (
854 name, real_value, expected_min, expected_max)
855 self.assertTrue(expected_min <= real_value <= expected_max, msg)
856
Klement Sekerad81ae412018-05-16 10:52:54 +0200857 def assert_packet_checksums_valid(self, packet,
858 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700859 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200860 self.logger.debug(
861 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200862 udp_layers = ['UDP', 'UDPerror']
863 checksum_fields = ['cksum', 'chksum']
864 checksums = []
865 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700866 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200867 while True:
868 layer = temp.getlayer(counter)
869 if layer:
870 for cf in checksum_fields:
871 if hasattr(layer, cf):
872 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100873 0 == getattr(layer, cf) and \
874 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200875 continue
876 delattr(layer, cf)
877 checksums.append((counter, cf))
878 else:
879 break
880 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200881 if 0 == len(checksums):
882 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700883 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200884 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200885 calc_sum = getattr(temp[layer], cf)
886 self.assert_equal(
887 getattr(received[layer], cf), calc_sum,
888 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
889 self.logger.debug(
890 "Checksum field `%s` on `%s` layer has correct value `%s`" %
891 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200892
893 def assert_checksum_valid(self, received_packet, layer,
894 field_name='chksum',
895 ignore_zero_checksum=False):
896 """ Check checksum of received packet on given layer """
897 received_packet_checksum = getattr(received_packet[layer], field_name)
898 if ignore_zero_checksum and 0 == received_packet_checksum:
899 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700900 recalculated = received_packet.__class__(
901 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200902 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700903 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +0200904 self.assert_equal(received_packet_checksum,
905 getattr(recalculated[layer], field_name),
906 "packet checksum on layer: %s" % layer)
907
908 def assert_ip_checksum_valid(self, received_packet,
909 ignore_zero_checksum=False):
910 self.assert_checksum_valid(received_packet, 'IP',
911 ignore_zero_checksum=ignore_zero_checksum)
912
913 def assert_tcp_checksum_valid(self, received_packet,
914 ignore_zero_checksum=False):
915 self.assert_checksum_valid(received_packet, 'TCP',
916 ignore_zero_checksum=ignore_zero_checksum)
917
918 def assert_udp_checksum_valid(self, received_packet,
919 ignore_zero_checksum=True):
920 self.assert_checksum_valid(received_packet, 'UDP',
921 ignore_zero_checksum=ignore_zero_checksum)
922
923 def assert_embedded_icmp_checksum_valid(self, received_packet):
924 if received_packet.haslayer(IPerror):
925 self.assert_checksum_valid(received_packet, 'IPerror')
926 if received_packet.haslayer(TCPerror):
927 self.assert_checksum_valid(received_packet, 'TCPerror')
928 if received_packet.haslayer(UDPerror):
929 self.assert_checksum_valid(received_packet, 'UDPerror',
930 ignore_zero_checksum=True)
931 if received_packet.haslayer(ICMPerror):
932 self.assert_checksum_valid(received_packet, 'ICMPerror')
933
934 def assert_icmp_checksum_valid(self, received_packet):
935 self.assert_checksum_valid(received_packet, 'ICMP')
936 self.assert_embedded_icmp_checksum_valid(received_packet)
937
938 def assert_icmpv6_checksum_valid(self, pkt):
939 if pkt.haslayer(ICMPv6DestUnreach):
940 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
941 self.assert_embedded_icmp_checksum_valid(pkt)
942 if pkt.haslayer(ICMPv6EchoRequest):
943 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
944 if pkt.haslayer(ICMPv6EchoReply):
945 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
946
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100947 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +0100948 if counter.startswith("/"):
949 counter_value = self.statistics.get_counter(counter)
950 self.assert_equal(counter_value, expected_value,
951 "packet counter `%s'" % counter)
952 else:
953 counters = self.vapi.cli("sh errors").split('\n')
954 counter_value = -1
955 for i in range(1, len(counters) - 1):
956 results = counters[i].split()
957 if results[1] == counter:
958 counter_value = int(results[0])
959 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100960
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100961 @classmethod
962 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700963
964 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
965 # * by Guido, only the main thread can be interrupted.
966 # */
967 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
968 if timeout == 0:
969 # yield quantum
970 if hasattr(os, 'sched_yield'):
971 os.sched_yield()
972 else:
973 time.sleep(0)
974 return
975
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100976 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800977 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000978 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100979 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000980 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800981 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700982 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800983 "slept for %es instead of ~%es!",
984 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000985 if hasattr(cls, 'logger'):
986 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800987 "Finished sleep (%s) - slept %es (wanted %es)",
988 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +0100989
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800990 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -0800991 self.vapi.cli("clear trace")
992 intf.add_stream(pkts)
993 self.pg_enable_capture(self.pg_interfaces)
994 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -0800995
996 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
997 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -0700998 if not timeout:
999 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001000 for i in self.pg_interfaces:
1001 i.get_capture(0, timeout=timeout)
1002 i.assert_nothing_captured(remark=remark)
1003 timeout = 0.1
1004
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001005 def send_and_expect(self, intf, pkts, output):
1006 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001007 rx = output.get_capture(len(pkts))
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001008 return rx
1009
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001010 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1011 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001012 rx = output.get_capture(len(pkts))
1013 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001014 if not timeout:
1015 timeout = 1
1016 for i in self.pg_interfaces:
1017 if i not in outputs:
1018 i.get_capture(0, timeout=timeout)
1019 i.assert_nothing_captured()
1020 timeout = 0.1
1021
Neale Ranns52fae862018-01-08 04:41:42 -08001022 return rx
1023
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001024 def runTest(self):
1025 """ unittest calls runTest when TestCase is instantiated without a
1026 test case. Use case: Writing unittests against VppTestCase"""
1027 pass
1028
Damjan Marionf56b77a2016-10-03 19:44:57 +02001029
juraj.linkes184870a2018-07-16 14:22:01 +02001030def get_testcase_doc_name(test):
1031 return getdoc(test.__class__).splitlines()[0]
1032
1033
Ole Trøan5ba91592018-11-22 10:01:09 +00001034def get_test_description(descriptions, test):
1035 short_description = test.shortDescription()
1036 if descriptions and short_description:
1037 return short_description
1038 else:
1039 return str(test)
1040
1041
juraj.linkes40dd73b2018-09-21 13:55:16 +02001042class TestCaseInfo(object):
1043 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1044 self.logger = logger
1045 self.tempdir = tempdir
1046 self.vpp_pid = vpp_pid
1047 self.vpp_bin_path = vpp_bin_path
1048 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001049
1050
Damjan Marionf56b77a2016-10-03 19:44:57 +02001051class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001052 """
1053 @property result_string
1054 String variable to store the test case result string.
1055 @property errors
1056 List variable containing 2-tuples of TestCase instances and strings
1057 holding formatted tracebacks. Each tuple represents a test which
1058 raised an unexpected exception.
1059 @property failures
1060 List variable containing 2-tuples of TestCase instances and strings
1061 holding formatted tracebacks. Each tuple represents a test where
1062 a failure was explicitly signalled using the TestCase.assert*()
1063 methods.
1064 """
1065
juraj.linkes40dd73b2018-09-21 13:55:16 +02001066 failed_test_cases_info = set()
1067 core_crash_test_cases_info = set()
1068 current_test_case_info = None
1069
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001070 def __init__(self, stream=None, descriptions=None, verbosity=None,
1071 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001072 """
Klement Sekerada505f62017-01-04 12:58:53 +01001073 :param stream File descriptor to store where to report test results.
1074 Set to the standard error stream by default.
1075 :param descriptions Boolean variable to store information if to use
1076 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001077 :param verbosity Integer variable to store required verbosity level.
1078 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001079 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001080 self.stream = stream
1081 self.descriptions = descriptions
1082 self.verbosity = verbosity
1083 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001084 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001085
Damjan Marionf56b77a2016-10-03 19:44:57 +02001086 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001087 """
1088 Record a test succeeded result
1089
1090 :param test:
1091
1092 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001093 if self.current_test_case_info:
1094 self.current_test_case_info.logger.debug(
1095 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1096 test._testMethodName,
1097 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001098 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001099 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001100
juraj.linkescae64f82018-09-19 15:01:47 +02001101 self.send_result_through_pipe(test, PASS)
1102
Klement Sekeraf62ae122016-10-11 11:47:09 +02001103 def addSkip(self, test, reason):
1104 """
1105 Record a test skipped.
1106
1107 :param test:
1108 :param reason:
1109
1110 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001111 if self.current_test_case_info:
1112 self.current_test_case_info.logger.debug(
1113 "--- addSkip() %s.%s(%s) called, reason is %s" %
1114 (test.__class__.__name__, test._testMethodName,
1115 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001116 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001117 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001118
juraj.linkescae64f82018-09-19 15:01:47 +02001119 self.send_result_through_pipe(test, SKIP)
1120
juraj.linkes40dd73b2018-09-21 13:55:16 +02001121 def symlink_failed(self):
1122 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001123 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001124 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001125 link_path = os.path.join(
1126 failed_dir,
1127 '%s-FAILED' %
1128 os.path.basename(self.current_test_case_info.tempdir))
1129 if self.current_test_case_info.logger:
1130 self.current_test_case_info.logger.debug(
1131 "creating a link to the failed test")
1132 self.current_test_case_info.logger.debug(
1133 "os.symlink(%s, %s)" %
1134 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001135 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001136 if self.current_test_case_info.logger:
1137 self.current_test_case_info.logger.debug(
1138 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001139 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001140 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001141
Klement Sekeraf413bef2017-08-15 07:09:02 +02001142 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001143 if self.current_test_case_info.logger:
1144 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001145
juraj.linkescae64f82018-09-19 15:01:47 +02001146 def send_result_through_pipe(self, test, result):
1147 if hasattr(self, 'test_framework_result_pipe'):
1148 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001149 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001150 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001151
juraj.linkes40dd73b2018-09-21 13:55:16 +02001152 def log_error(self, test, err, fn_name):
1153 if self.current_test_case_info:
1154 if isinstance(test, unittest.suite._ErrorHolder):
1155 test_name = test.description
1156 else:
1157 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1158 test._testMethodName,
1159 test._testMethodDoc)
1160 self.current_test_case_info.logger.debug(
1161 "--- %s() %s called, err is %s" %
1162 (fn_name, test_name, err))
1163 self.current_test_case_info.logger.debug(
1164 "formatted exception is:\n%s" %
1165 "".join(format_exception(*err)))
1166
1167 def add_error(self, test, err, unittest_fn, error_type):
1168 if error_type == FAIL:
1169 self.log_error(test, err, 'addFailure')
1170 error_type_str = colorize("FAIL", RED)
1171 elif error_type == ERROR:
1172 self.log_error(test, err, 'addError')
1173 error_type_str = colorize("ERROR", RED)
1174 else:
1175 raise Exception('Error type %s cannot be used to record an '
1176 'error or a failure' % error_type)
1177
1178 unittest_fn(self, test, err)
1179 if self.current_test_case_info:
1180 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1181 (error_type_str,
1182 self.current_test_case_info.tempdir)
1183 self.symlink_failed()
1184 self.failed_test_cases_info.add(self.current_test_case_info)
1185 if is_core_present(self.current_test_case_info.tempdir):
1186 if not self.current_test_case_info.core_crash_test:
1187 if isinstance(test, unittest.suite._ErrorHolder):
1188 test_name = str(test)
1189 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001190 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001191 get_testcase_doc_name(test), test.id())
1192 self.current_test_case_info.core_crash_test = test_name
1193 self.core_crash_test_cases_info.add(
1194 self.current_test_case_info)
1195 else:
1196 self.result_string = '%s [no temp dir]' % error_type_str
1197
1198 self.send_result_through_pipe(test, error_type)
1199
Damjan Marionf56b77a2016-10-03 19:44:57 +02001200 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001201 """
1202 Record a test failed result
1203
1204 :param test:
1205 :param err: error message
1206
1207 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001208 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001209
Damjan Marionf56b77a2016-10-03 19:44:57 +02001210 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001211 """
1212 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001213
Klement Sekeraf62ae122016-10-11 11:47:09 +02001214 :param test:
1215 :param err: error message
1216
1217 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001218 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001219
Damjan Marionf56b77a2016-10-03 19:44:57 +02001220 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001221 """
1222 Get test description
1223
1224 :param test:
1225 :returns: test description
1226
1227 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001228 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001229
Damjan Marionf56b77a2016-10-03 19:44:57 +02001230 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001231 """
1232 Start a test
1233
1234 :param test:
1235
1236 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001237
1238 def print_header(test):
1239 if not hasattr(test.__class__, '_header_printed'):
1240 print(double_line_delim)
1241 print(colorize(getdoc(test).splitlines()[0], GREEN))
1242 print(double_line_delim)
1243 test.__class__._header_printed = True
1244
1245 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001246
Damjan Marionf56b77a2016-10-03 19:44:57 +02001247 unittest.TestResult.startTest(self, test)
1248 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001249 self.stream.writeln(
1250 "Starting " + self.getDescription(test) + " ...")
1251 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001252
Damjan Marionf56b77a2016-10-03 19:44:57 +02001253 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001254 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001255 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001256
1257 :param test:
1258
1259 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001260 unittest.TestResult.stopTest(self, test)
1261 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001262 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001263 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001264 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001265 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001266 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001267 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001268 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001269
1270 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001271
Damjan Marionf56b77a2016-10-03 19:44:57 +02001272 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001273 """
1274 Print errors from running the test case
1275 """
juraj.linkesabec0122018-11-16 17:28:56 +01001276 if len(self.errors) > 0 or len(self.failures) > 0:
1277 self.stream.writeln()
1278 self.printErrorList('ERROR', self.errors)
1279 self.printErrorList('FAIL', self.failures)
1280
1281 # ^^ that is the last output from unittest before summary
1282 if not self.runner.print_summary:
1283 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1284 self.stream = devnull
1285 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001286
Damjan Marionf56b77a2016-10-03 19:44:57 +02001287 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001288 """
1289 Print error list to the output stream together with error type
1290 and test case description.
1291
1292 :param flavour: error type
1293 :param errors: iterable errors
1294
1295 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001296 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001297 self.stream.writeln(double_line_delim)
1298 self.stream.writeln("%s: %s" %
1299 (flavour, self.getDescription(test)))
1300 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001301 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001302
1303
Damjan Marionf56b77a2016-10-03 19:44:57 +02001304class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001305 """
Klement Sekera104543f2017-02-03 07:29:43 +01001306 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001307 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001308
Klement Sekeraf62ae122016-10-11 11:47:09 +02001309 @property
1310 def resultclass(self):
1311 """Class maintaining the results of the tests"""
1312 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001313
juraj.linkes184870a2018-07-16 14:22:01 +02001314 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001315 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001316 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001317 # ignore stream setting here, use hard-coded stdout to be in sync
1318 # with prints from VppTestCase methods ...
1319 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1320 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001321 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001322 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001323
juraj.linkesabec0122018-11-16 17:28:56 +01001324 self.orig_stream = self.stream
1325 self.resultclass.test_framework_result_pipe = result_pipe
1326
1327 self.print_summary = print_summary
1328
1329 def _makeResult(self):
1330 return self.resultclass(self.stream,
1331 self.descriptions,
1332 self.verbosity,
1333 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001334
Damjan Marionf56b77a2016-10-03 19:44:57 +02001335 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001336 """
1337 Run the tests
1338
1339 :param test:
1340
1341 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001342 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001343
1344 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001345 if not self.print_summary:
1346 self.stream = self.orig_stream
1347 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001348 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001349
1350
1351class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001352 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001353 self.logger = logger
1354 self.args = args
1355 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001356 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001357 super(Worker, self).__init__()
1358
1359 def run(self):
1360 executable = self.args[0]
1361 self.logger.debug("Running executable w/args `%s'" % self.args)
1362 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001363 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001364 env["CK_LOG_FILE_NAME"] = "-"
1365 self.process = subprocess.Popen(
1366 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1367 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1368 out, err = self.process.communicate()
1369 self.logger.debug("Finished running `%s'" % executable)
1370 self.logger.info("Return code is `%s'" % self.process.returncode)
1371 self.logger.info(single_line_delim)
1372 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1373 self.logger.info(single_line_delim)
1374 self.logger.info(out)
1375 self.logger.info(single_line_delim)
1376 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1377 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001378 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001379 self.logger.info(single_line_delim)
1380 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001381
1382if __name__ == '__main__':
1383 pass