blob: dce477d16d2fc4a0567c41e35d09ead7cc8315eb [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
Neale Ranns192b13f2019-03-15 02:16:20 -070028from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000029from vpp_papi_provider import VppPapiProvider
30from vpp_papi.vpp_stats import VPPStats
31from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
32 get_logger, colorize
33from vpp_object import VppObjectRegistry
34from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020035from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
36from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
37from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080038
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010039if os.name == 'posix' and sys.version_info[0] < 3:
40 # using subprocess32 is recommended by python official documentation
41 # @ https://docs.python.org/2/library/subprocess.html
42 import subprocess32 as subprocess
43else:
44 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020045
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080046# Python2/3 compatible
47try:
48 input = raw_input
49except NameError:
50 pass
51
juraj.linkescae64f82018-09-19 15:01:47 +020052PASS = 0
53FAIL = 1
54ERROR = 2
55SKIP = 3
56TEST_RUN = 4
57
Klement Sekeraebbaf552018-02-17 13:41:33 +010058debug_framework = False
59if os.getenv('TEST_DEBUG', "0") == "1":
60 debug_framework = True
61 import debug_internal
62
Klement Sekeraf62ae122016-10-11 11:47:09 +020063"""
64 Test framework module.
65
66 The module provides a set of tools for constructing and running tests and
67 representing the results.
68"""
69
Klement Sekeraf62ae122016-10-11 11:47:09 +020070
Damjan Marionf56b77a2016-10-03 19:44:57 +020071class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020072 """Private class to create packet info object.
73
74 Help process information about the next packet.
75 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020076 """
Matej Klotton86d87c42016-11-11 11:38:55 +010077 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020078 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010079 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020080 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010081 #: Store the index of the destination packet generator interface
82 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020083 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010084 #: Store expected ip version
85 ip = -1
86 #: Store expected upper protocol
87 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010088 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020089 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020090
Matej Klotton16a14cd2016-12-07 15:09:13 +010091 def __eq__(self, other):
92 index = self.index == other.index
93 src = self.src == other.src
94 dst = self.dst == other.dst
95 data = self.data == other.data
96 return index and src and dst and data
97
Klement Sekeraf62ae122016-10-11 11:47:09 +020098
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010099def pump_output(testclass):
100 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100101 stdout_fragment = ""
102 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400103 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100104 readable = select.select([testclass.vpp.stdout.fileno(),
105 testclass.vpp.stderr.fileno(),
106 testclass.pump_thread_wakeup_pipe[0]],
107 [], [])[0]
108 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100109 read = os.read(testclass.vpp.stdout.fileno(), 102400)
110 if len(read) > 0:
111 split = read.splitlines(True)
112 if len(stdout_fragment) > 0:
113 split[0] = "%s%s" % (stdout_fragment, split[0])
114 if len(split) > 0 and split[-1].endswith("\n"):
115 limit = None
116 else:
117 limit = -1
118 stdout_fragment = split[-1]
119 testclass.vpp_stdout_deque.extend(split[:limit])
120 if not testclass.cache_vpp_output:
121 for line in split[:limit]:
122 testclass.logger.debug(
123 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100124 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100125 read = os.read(testclass.vpp.stderr.fileno(), 102400)
126 if len(read) > 0:
127 split = read.splitlines(True)
128 if len(stderr_fragment) > 0:
129 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan7f991832018-12-06 17:35:12 +0100130 if len(split) > 0 and split[-1].endswith(b"\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100131 limit = None
132 else:
133 limit = -1
134 stderr_fragment = split[-1]
135 testclass.vpp_stderr_deque.extend(split[:limit])
136 if not testclass.cache_vpp_output:
137 for line in split[:limit]:
138 testclass.logger.debug(
139 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800140 # ignoring the dummy pipe here intentionally - the
141 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200142
143
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800144def _is_skip_aarch64_set():
juraj.linkes68ebc832018-11-29 09:37:08 +0100145 return os.getenv('SKIP_AARCH64', 'n').lower() in ('yes', 'y', '1')
146
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800147is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100148
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800149
150def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100151 return platform.machine() == 'aarch64'
152
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800153is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100154
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800155
156def _running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100157 s = os.getenv("EXTENDED_TESTS", "n")
158 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100159
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800160running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100161
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800162
163def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100164 os_id = os.getenv("OS_ID", "")
165 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200166
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800167running_on_centos = _running_on_centos
168
Klement Sekerad3e671e2017-09-29 12:36:37 +0200169
Klement Sekera909a6a12017-08-08 04:33:53 +0200170class KeepAliveReporter(object):
171 """
172 Singleton object which reports test start to parent process
173 """
174 _shared_state = {}
175
176 def __init__(self):
177 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800178 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200179
180 @property
181 def pipe(self):
182 return self._pipe
183
184 @pipe.setter
185 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800186 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200187 raise Exception("Internal error - pipe should only be set once.")
188 self._pipe = pipe
189
juraj.linkes40dd73b2018-09-21 13:55:16 +0200190 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200191 """
192 Write current test tmpdir & desc to keep-alive pipe to signal liveness
193 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200194 if self.pipe is None:
195 # if not running forked..
196 return
197
Klement Sekera909a6a12017-08-08 04:33:53 +0200198 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200199 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200200 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200201 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200202
Dave Wallacee2efd122017-09-30 22:04:21 -0400203 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200204
205
Damjan Marionf56b77a2016-10-03 19:44:57 +0200206class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100207 """This subclass is a base class for VPP test cases that are implemented as
208 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200209 """
210
Ole Troana45dc072018-12-21 16:04:22 +0100211 extra_vpp_punt_config = []
212 extra_vpp_plugin_config = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100213
Klement Sekeraf62ae122016-10-11 11:47:09 +0200214 @property
215 def packet_infos(self):
216 """List of packet infos"""
217 return self._packet_infos
218
Klement Sekeradab231a2016-12-21 08:50:14 +0100219 @classmethod
220 def get_packet_count_for_if_idx(cls, dst_if_index):
221 """Get the number of packet info for specified destination if index"""
222 if dst_if_index in cls._packet_count_for_dst_if_idx:
223 return cls._packet_count_for_dst_if_idx[dst_if_index]
224 else:
225 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200226
227 @classmethod
228 def instance(cls):
229 """Return the instance of this testcase"""
230 return cls.test_instance
231
Damjan Marionf56b77a2016-10-03 19:44:57 +0200232 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200233 def set_debug_flags(cls, d):
234 cls.debug_core = False
235 cls.debug_gdb = False
236 cls.debug_gdbserver = False
237 if d is None:
238 return
239 dl = d.lower()
240 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200241 cls.debug_core = True
242 elif dl == "gdb":
243 cls.debug_gdb = True
244 elif dl == "gdbserver":
245 cls.debug_gdbserver = True
246 else:
247 raise Exception("Unrecognized DEBUG option: '%s'" % d)
248
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800249 @staticmethod
250 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200251 cpu_usage_list = [set(range(psutil.cpu_count()))]
252 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
253 if 'vpp_main' == p.info['name']]
254 for vpp_process in vpp_processes:
255 for cpu_usage_set in cpu_usage_list:
256 try:
257 cpu_num = vpp_process.cpu_num()
258 if cpu_num in cpu_usage_set:
259 cpu_usage_set_index = cpu_usage_list.index(
260 cpu_usage_set)
261 if cpu_usage_set_index == len(cpu_usage_list) - 1:
262 cpu_usage_list.append({cpu_num})
263 else:
264 cpu_usage_list[cpu_usage_set_index + 1].add(
265 cpu_num)
266 cpu_usage_set.remove(cpu_num)
267 break
268 except psutil.NoSuchProcess:
269 pass
270
271 for cpu_usage_set in cpu_usage_list:
272 if len(cpu_usage_set) > 0:
273 min_usage_set = cpu_usage_set
274 break
275
276 return random.choice(tuple(min_usage_set))
277
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800278 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200279 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200280 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100281 s = os.getenv("STEP", "n")
282 cls.step = True if s.lower() in ("y", "yes", "1") else False
283 d = os.getenv("DEBUG", None)
284 c = os.getenv("CACHE_OUTPUT", "1")
285 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200286 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100287 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
288 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100289 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
290 plugin_path = None
291 if cls.plugin_path is not None:
292 if cls.extern_plugin_path is not None:
293 plugin_path = "%s:%s" % (
294 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100295 else:
296 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100297 elif cls.extern_plugin_path is not None:
298 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100299 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100300 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100301 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100302 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100303 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100304 if size is not None:
305 coredump_size = "coredump-size %s" % size
306 if coredump_size is None:
307 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200308
Ole Troana45dc072018-12-21 16:04:22 +0100309 cpu_core_number = cls.get_least_used_cpu()
juraj.linkes184870a2018-07-16 14:22:01 +0200310
Ole Troana45dc072018-12-21 16:04:22 +0100311 cls.vpp_cmdline = [cls.vpp_bin, "unix",
312 "{", "nodaemon", debug_cli, "full-coredump",
313 coredump_size, "runtime-dir", cls.tempdir, "}",
314 "api-trace", "{", "on", "}", "api-segment", "{",
315 "prefix", cls.shm_prefix, "}", "cpu", "{",
316 "main-core", str(cpu_core_number), "}", "statseg",
317 "{", "socket-name", cls.stats_sock, "}", "plugins",
318 "{", "plugin", "dpdk_plugin.so", "{", "disable",
319 "}", "plugin", "unittest_plugin.so", "{", "enable",
320 "}"] + cls.extra_vpp_plugin_config + ["}", ]
321 if cls.extra_vpp_punt_config is not None:
322 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100323 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100324 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100325 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
326 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200327
328 @classmethod
329 def wait_for_enter(cls):
330 if cls.debug_gdbserver:
331 print(double_line_delim)
332 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
333 elif cls.debug_gdb:
334 print(double_line_delim)
335 print("Spawned VPP with PID: %d" % cls.vpp.pid)
336 else:
337 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
338 return
339 print(single_line_delim)
340 print("You can debug the VPP using e.g.:")
341 if cls.debug_gdbserver:
342 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
343 print("Now is the time to attach a gdb by running the above "
344 "command, set up breakpoints etc. and then resume VPP from "
345 "within gdb by issuing the 'continue' command")
346 elif cls.debug_gdb:
347 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
348 print("Now is the time to attach a gdb by running the above "
349 "command and set up breakpoints etc.")
350 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800351 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200352
353 @classmethod
354 def run_vpp(cls):
355 cmdline = cls.vpp_cmdline
356
357 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100358 gdbserver = '/usr/bin/gdbserver'
359 if not os.path.isfile(gdbserver) or \
360 not os.access(gdbserver, os.X_OK):
361 raise Exception("gdbserver binary '%s' does not exist or is "
362 "not executable" % gdbserver)
363
364 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200365 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
366
Klement Sekera931be3a2016-11-03 05:36:01 +0100367 try:
368 cls.vpp = subprocess.Popen(cmdline,
369 stdout=subprocess.PIPE,
370 stderr=subprocess.PIPE,
371 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800372 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800373 cls.logger.critical("Subprocess returned with non-0 return code: ("
374 "%s)", e.returncode)
375 raise
376 except OSError as e:
377 cls.logger.critical("Subprocess returned with OS error: "
378 "(%s) %s", e.errno, e.strerror)
379 raise
380 except Exception as e:
381 cls.logger.exception("Subprocess returned unexpected from "
382 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100383 raise
384
Klement Sekera277b89c2016-10-28 13:20:27 +0200385 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100386
Damjan Marionf56b77a2016-10-03 19:44:57 +0200387 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200388 def wait_for_stats_socket(cls):
389 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800390 ok = False
391 while time.time() < deadline or \
392 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200393 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800394 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200395 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700396 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800397 if not ok:
398 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200399
400 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200401 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200402 """
403 Perform class setup before running the testcase
404 Remove shared memory files, start vpp and connect the vpp-api
405 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800406 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100407 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100408 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100409 cls.logger = get_logger(cls.__name__)
410 if hasattr(cls, 'parallel_handler'):
411 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100412 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200413 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200414 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200415 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200416 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
417 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100418 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
419 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200420 cls.file_handler.setLevel(DEBUG)
421 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200422 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200423 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200424 cls.logger.info("Temporary dir is %s, shm prefix is %s",
425 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200426 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100427 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100428 cls._captures = []
429 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200430 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100431 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100432 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200433 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200434 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200435 # need to catch exceptions here because if we raise, then the cleanup
436 # doesn't get called and we might end with a zombie vpp
437 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200438 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200439 cls.reporter.send_keep_alive(cls, 'setUpClass')
440 VppTestResult.current_test_case_info = TestCaseInfo(
441 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100442 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100443 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100444 cls.pump_thread_stop_flag = Event()
445 cls.pump_thread_wakeup_pipe = os.pipe()
446 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100447 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100448 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200449 if cls.debug_gdb or cls.debug_gdbserver:
450 read_timeout = 0
451 else:
452 read_timeout = 5
453 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
454 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100455 if cls.step:
456 hook = StepHook(cls)
457 else:
458 hook = PollHook(cls)
459 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200460 cls.wait_for_stats_socket()
461 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200462 try:
463 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100464 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200465 cls.vpp_startup_failed = True
466 cls.logger.critical(
467 "VPP died shortly after startup, check the"
468 " output to standard error for possible cause")
469 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100470 try:
471 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100472 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100473 try:
474 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100475 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100476 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100477 if cls.debug_gdbserver:
478 print(colorize("You're running VPP inside gdbserver but "
479 "VPP-API connection failed, did you forget "
480 "to 'continue' VPP from within gdb?", RED))
481 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100482 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100483 try:
484 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100485 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100486 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100487 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200488
Damjan Marionf56b77a2016-10-03 19:44:57 +0200489 @classmethod
490 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200491 """
492 Disconnect vpp-api, kill vpp and cleanup shared memory files
493 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200494 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
495 cls.vpp.poll()
496 if cls.vpp.returncode is None:
497 print(double_line_delim)
498 print("VPP or GDB server is still running")
499 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800500 input("When done debugging, press ENTER to kill the "
501 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200502
juraj.linkes184870a2018-07-16 14:22:01 +0200503 # first signal that we want to stop the pump thread, then wake it up
504 if hasattr(cls, 'pump_thread_stop_flag'):
505 cls.pump_thread_stop_flag.set()
506 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100507 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100508 if hasattr(cls, 'pump_thread'):
509 cls.logger.debug("Waiting for pump thread to stop")
510 cls.pump_thread.join()
511 if hasattr(cls, 'vpp_stderr_reader_thread'):
512 cls.logger.debug("Waiting for stdderr pump to stop")
513 cls.vpp_stderr_reader_thread.join()
514
Klement Sekeraf62ae122016-10-11 11:47:09 +0200515 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100516 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700517 cls.logger.debug("Disconnecting class vapi client on %s",
518 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100519 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700520 cls.logger.debug("Deleting class vapi attribute on %s",
521 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100522 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200523 cls.vpp.poll()
524 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100525 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200526 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100527 cls.logger.debug("Waiting for vpp to die")
528 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700529 cls.logger.debug("Deleting class vpp attribute on %s",
530 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200531 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200532
Klement Sekera3747c752017-04-10 06:30:17 +0200533 if cls.vpp_startup_failed:
534 stdout_log = cls.logger.info
535 stderr_log = cls.logger.critical
536 else:
537 stdout_log = cls.logger.info
538 stderr_log = cls.logger.info
539
Klement Sekerae4504c62016-12-08 10:16:41 +0100540 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200541 stdout_log(single_line_delim)
542 stdout_log('VPP output to stdout while running %s:', cls.__name__)
543 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100544 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200545 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
546 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200547 stdout_log('\n%s', vpp_output)
548 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200549
Klement Sekerae4504c62016-12-08 10:16:41 +0100550 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200551 stderr_log(single_line_delim)
552 stderr_log('VPP output to stderr while running %s:', cls.__name__)
553 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100554 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200555 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
556 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200557 stderr_log('\n%s', vpp_output)
558 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200559
Damjan Marionf56b77a2016-10-03 19:44:57 +0200560 @classmethod
561 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200562 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200563 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200564 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200565 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100566 cls.reset_packet_infos()
567 if debug_framework:
568 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200569
Damjan Marionf56b77a2016-10-03 19:44:57 +0200570 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200571 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100572 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
573 (self.__class__.__name__, self._testMethodName,
574 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200575 if not self.vpp_dead:
Klement Sekerad91fa612019-01-15 13:25:09 +0100576 self.logger.debug(self.vapi.cli("show trace max 1000"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700577 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200578 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200579 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200580 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800581 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100582 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500583 # Save/Dump VPP api trace log
584 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
585 tmp_api_trace = "/tmp/%s" % api_trace
586 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
587 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
588 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
589 vpp_api_trace_log))
590 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500591 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500592 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100593 else:
594 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200595
Damjan Marionf56b77a2016-10-03 19:44:57 +0200596 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200597 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800598 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200599 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100600 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
601 (self.__class__.__name__, self._testMethodName,
602 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100603 if self.vpp_dead:
604 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100605 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100606 self.vpp_stdout_deque.append(
607 "--- test setUp() for %s.%s(%s) starts here ---\n" %
608 (self.__class__.__name__, self._testMethodName,
609 self._testMethodDoc))
610 self.vpp_stderr_deque.append(
611 "--- test setUp() for %s.%s(%s) starts here ---\n" %
612 (self.__class__.__name__, self._testMethodName,
613 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200614 self.vapi.cli("clear trace")
615 # store the test instance inside the test class - so that objects
616 # holding the class can access instance methods (like assertEqual)
617 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200618
Damjan Marionf56b77a2016-10-03 19:44:57 +0200619 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200620 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200621 """
622 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200623
Klement Sekera75e7d132017-09-20 08:26:30 +0200624 :param interfaces: iterable interface indexes (if None,
625 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200626
Klement Sekeraf62ae122016-10-11 11:47:09 +0200627 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200628 if interfaces is None:
629 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200630 for i in interfaces:
631 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200632
Damjan Marionf56b77a2016-10-03 19:44:57 +0200633 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100634 def register_capture(cls, cap_name):
635 """ Register a capture in the testclass """
636 # add to the list of captures with current timestamp
637 cls._captures.append((time.time(), cap_name))
638 # filter out from zombies
639 cls._zombie_captures = [(stamp, name)
640 for (stamp, name) in cls._zombie_captures
641 if name != cap_name]
642
643 @classmethod
644 def pg_start(cls):
645 """ Remove any zombie captures and enable the packet generator """
646 # how long before capture is allowed to be deleted - otherwise vpp
647 # crashes - 100ms seems enough (this shouldn't be needed at all)
648 capture_ttl = 0.1
649 now = time.time()
650 for stamp, cap_name in cls._zombie_captures:
651 wait = stamp + capture_ttl - now
652 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100653 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100654 now = time.time()
655 cls.logger.debug("Removing zombie capture %s" % cap_name)
656 cls.vapi.cli('packet-generator delete %s' % cap_name)
657
Klement Sekerad91fa612019-01-15 13:25:09 +0100658 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200659 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100660 cls._zombie_captures = cls._captures
661 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200662
Damjan Marionf56b77a2016-10-03 19:44:57 +0200663 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200664 def create_pg_interfaces(cls, interfaces):
665 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100666 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200667
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100668 :param interfaces: iterable indexes of the interfaces.
669 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200670
Klement Sekeraf62ae122016-10-11 11:47:09 +0200671 """
672 result = []
673 for i in interfaces:
674 intf = VppPGInterface(cls, i)
675 setattr(cls, intf.name, intf)
676 result.append(intf)
677 cls.pg_interfaces = result
678 return result
679
Matej Klotton0178d522016-11-04 11:11:44 +0100680 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200681 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100682 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100683 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100684
Klement Sekerab9ef2732018-06-24 22:49:33 +0200685 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100686 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100687 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200688 result = [VppLoInterface(cls) for i in range(count)]
689 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100690 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100691 cls.lo_interfaces = result
692 return result
693
Neale Ranns192b13f2019-03-15 02:16:20 -0700694 @classmethod
695 def create_bvi_interfaces(cls, count):
696 """
697 Create BVI interfaces.
698
699 :param count: number of interfaces created.
700 :returns: List of created interfaces.
701 """
702 result = [VppBviInterface(cls) for i in range(count)]
703 for intf in result:
704 setattr(cls, intf.name, intf)
705 cls.bvi_interfaces = result
706 return result
707
Damjan Marionf56b77a2016-10-03 19:44:57 +0200708 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200709 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200710 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200711 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200712 NOTE: Currently works only when Raw layer is present.
713
714 :param packet: packet
715 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200716 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200717
718 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200719 packet_len = len(packet) + 4
720 extend = size - packet_len
721 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200722 num = (extend / len(padding)) + 1
723 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200724
Klement Sekeradab231a2016-12-21 08:50:14 +0100725 @classmethod
726 def reset_packet_infos(cls):
727 """ Reset the list of packet info objects and packet counts to zero """
728 cls._packet_infos = {}
729 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200730
Klement Sekeradab231a2016-12-21 08:50:14 +0100731 @classmethod
732 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200733 """
734 Create packet info object containing the source and destination indexes
735 and add it to the testcase's packet info list
736
Klement Sekeradab231a2016-12-21 08:50:14 +0100737 :param VppInterface src_if: source interface
738 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200739
740 :returns: _PacketInfo object
741
742 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200743 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100744 info.index = len(cls._packet_infos)
745 info.src = src_if.sw_if_index
746 info.dst = dst_if.sw_if_index
747 if isinstance(dst_if, VppSubInterface):
748 dst_idx = dst_if.parent.sw_if_index
749 else:
750 dst_idx = dst_if.sw_if_index
751 if dst_idx in cls._packet_count_for_dst_if_idx:
752 cls._packet_count_for_dst_if_idx[dst_idx] += 1
753 else:
754 cls._packet_count_for_dst_if_idx[dst_idx] = 1
755 cls._packet_infos[info.index] = info
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 @staticmethod
759 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200760 """
761 Convert _PacketInfo object to packet payload
762
763 :param info: _PacketInfo object
764
765 :returns: string containing serialized data from packet info
766 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100767 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
768 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769
Damjan Marionf56b77a2016-10-03 19:44:57 +0200770 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800771 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200772 """
773 Convert packet payload to _PacketInfo object
774
775 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700776 :type payload: <class 'scapy.packet.Raw'>
777 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800778 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700779 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200780 :returns: _PacketInfo object containing de-serialized data from payload
781
782 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800783 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784 info = _PacketInfo()
785 info.index = int(numbers[0])
786 info.src = int(numbers[1])
787 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100788 info.ip = int(numbers[3])
789 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200790 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200791
Damjan Marionf56b77a2016-10-03 19:44:57 +0200792 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200793 """
794 Iterate over the packet info list stored in the testcase
795 Start iteration with first element if info is None
796 Continue based on index in info if info is specified
797
798 :param info: info or None
799 :returns: next info in list or None if no more infos
800 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200801 if info is None:
802 next_index = 0
803 else:
804 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100805 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200806 return None
807 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100808 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200809
Klement Sekeraf62ae122016-10-11 11:47:09 +0200810 def get_next_packet_info_for_interface(self, src_index, info):
811 """
812 Search the packet info list for the next packet info with same source
813 interface index
814
815 :param src_index: source interface index to search for
816 :param info: packet info - where to start the search
817 :returns: packet info or None
818
819 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200820 while True:
821 info = self.get_next_packet_info(info)
822 if info is None:
823 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200824 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200825 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200826
Klement Sekeraf62ae122016-10-11 11:47:09 +0200827 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
828 """
829 Search the packet info list for the next packet info with same source
830 and destination interface indexes
831
832 :param src_index: source interface index to search for
833 :param dst_index: destination interface index to search for
834 :param info: packet info - where to start the search
835 :returns: packet info or None
836
837 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200838 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200839 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200840 if info is None:
841 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200842 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200843 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200844
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200845 def assert_equal(self, real_value, expected_value, name_or_class=None):
846 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100847 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200848 return
849 try:
850 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
851 msg = msg % (getdoc(name_or_class).strip(),
852 real_value, str(name_or_class(real_value)),
853 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100854 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200855 msg = "Invalid %s: %s does not match expected value %s" % (
856 name_or_class, real_value, expected_value)
857
858 self.assertEqual(real_value, expected_value, msg)
859
Klement Sekerab17dd962017-01-09 07:43:48 +0100860 def assert_in_range(self,
861 real_value,
862 expected_min,
863 expected_max,
864 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200865 if name is None:
866 msg = None
867 else:
868 msg = "Invalid %s: %s out of range <%s,%s>" % (
869 name, real_value, expected_min, expected_max)
870 self.assertTrue(expected_min <= real_value <= expected_max, msg)
871
Klement Sekerad81ae412018-05-16 10:52:54 +0200872 def assert_packet_checksums_valid(self, packet,
873 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700874 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200875 self.logger.debug(
876 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200877 udp_layers = ['UDP', 'UDPerror']
878 checksum_fields = ['cksum', 'chksum']
879 checksums = []
880 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700881 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200882 while True:
883 layer = temp.getlayer(counter)
884 if layer:
885 for cf in checksum_fields:
886 if hasattr(layer, cf):
887 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100888 0 == getattr(layer, cf) and \
889 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200890 continue
891 delattr(layer, cf)
892 checksums.append((counter, cf))
893 else:
894 break
895 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200896 if 0 == len(checksums):
897 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700898 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200899 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200900 calc_sum = getattr(temp[layer], cf)
901 self.assert_equal(
902 getattr(received[layer], cf), calc_sum,
903 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
904 self.logger.debug(
905 "Checksum field `%s` on `%s` layer has correct value `%s`" %
906 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200907
908 def assert_checksum_valid(self, received_packet, layer,
909 field_name='chksum',
910 ignore_zero_checksum=False):
911 """ Check checksum of received packet on given layer """
912 received_packet_checksum = getattr(received_packet[layer], field_name)
913 if ignore_zero_checksum and 0 == received_packet_checksum:
914 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700915 recalculated = received_packet.__class__(
916 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200917 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700918 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +0200919 self.assert_equal(received_packet_checksum,
920 getattr(recalculated[layer], field_name),
921 "packet checksum on layer: %s" % layer)
922
923 def assert_ip_checksum_valid(self, received_packet,
924 ignore_zero_checksum=False):
925 self.assert_checksum_valid(received_packet, 'IP',
926 ignore_zero_checksum=ignore_zero_checksum)
927
928 def assert_tcp_checksum_valid(self, received_packet,
929 ignore_zero_checksum=False):
930 self.assert_checksum_valid(received_packet, 'TCP',
931 ignore_zero_checksum=ignore_zero_checksum)
932
933 def assert_udp_checksum_valid(self, received_packet,
934 ignore_zero_checksum=True):
935 self.assert_checksum_valid(received_packet, 'UDP',
936 ignore_zero_checksum=ignore_zero_checksum)
937
938 def assert_embedded_icmp_checksum_valid(self, received_packet):
939 if received_packet.haslayer(IPerror):
940 self.assert_checksum_valid(received_packet, 'IPerror')
941 if received_packet.haslayer(TCPerror):
942 self.assert_checksum_valid(received_packet, 'TCPerror')
943 if received_packet.haslayer(UDPerror):
944 self.assert_checksum_valid(received_packet, 'UDPerror',
945 ignore_zero_checksum=True)
946 if received_packet.haslayer(ICMPerror):
947 self.assert_checksum_valid(received_packet, 'ICMPerror')
948
949 def assert_icmp_checksum_valid(self, received_packet):
950 self.assert_checksum_valid(received_packet, 'ICMP')
951 self.assert_embedded_icmp_checksum_valid(received_packet)
952
953 def assert_icmpv6_checksum_valid(self, pkt):
954 if pkt.haslayer(ICMPv6DestUnreach):
955 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
956 self.assert_embedded_icmp_checksum_valid(pkt)
957 if pkt.haslayer(ICMPv6EchoRequest):
958 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
959 if pkt.haslayer(ICMPv6EchoReply):
960 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
961
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100962 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +0100963 if counter.startswith("/"):
964 counter_value = self.statistics.get_counter(counter)
965 self.assert_equal(counter_value, expected_value,
966 "packet counter `%s'" % counter)
967 else:
968 counters = self.vapi.cli("sh errors").split('\n')
969 counter_value = -1
970 for i in range(1, len(counters) - 1):
971 results = counters[i].split()
972 if results[1] == counter:
973 counter_value = int(results[0])
974 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100975
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100976 @classmethod
977 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700978
979 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
980 # * by Guido, only the main thread can be interrupted.
981 # */
982 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
983 if timeout == 0:
984 # yield quantum
985 if hasattr(os, 'sched_yield'):
986 os.sched_yield()
987 else:
988 time.sleep(0)
989 return
990
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100991 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800992 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000993 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100994 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000995 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800996 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700997 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800998 "slept for %es instead of ~%es!",
999 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001000 if hasattr(cls, 'logger'):
1001 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001002 "Finished sleep (%s) - slept %es (wanted %es)",
1003 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001004
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001005 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001006 self.vapi.cli("clear trace")
1007 intf.add_stream(pkts)
1008 self.pg_enable_capture(self.pg_interfaces)
1009 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001010
1011 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1012 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001013 if not timeout:
1014 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001015 for i in self.pg_interfaces:
1016 i.get_capture(0, timeout=timeout)
1017 i.assert_nothing_captured(remark=remark)
1018 timeout = 0.1
1019
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001020 def send_and_expect(self, intf, pkts, output):
1021 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001022 rx = output.get_capture(len(pkts))
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001023 return rx
1024
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001025 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1026 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001027 rx = output.get_capture(len(pkts))
1028 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001029 if not timeout:
1030 timeout = 1
1031 for i in self.pg_interfaces:
1032 if i not in outputs:
1033 i.get_capture(0, timeout=timeout)
1034 i.assert_nothing_captured()
1035 timeout = 0.1
1036
Neale Ranns52fae862018-01-08 04:41:42 -08001037 return rx
1038
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001039 def runTest(self):
1040 """ unittest calls runTest when TestCase is instantiated without a
1041 test case. Use case: Writing unittests against VppTestCase"""
1042 pass
1043
Damjan Marionf56b77a2016-10-03 19:44:57 +02001044
juraj.linkes184870a2018-07-16 14:22:01 +02001045def get_testcase_doc_name(test):
1046 return getdoc(test.__class__).splitlines()[0]
1047
1048
Ole Trøan5ba91592018-11-22 10:01:09 +00001049def get_test_description(descriptions, test):
1050 short_description = test.shortDescription()
1051 if descriptions and short_description:
1052 return short_description
1053 else:
1054 return str(test)
1055
1056
juraj.linkes40dd73b2018-09-21 13:55:16 +02001057class TestCaseInfo(object):
1058 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1059 self.logger = logger
1060 self.tempdir = tempdir
1061 self.vpp_pid = vpp_pid
1062 self.vpp_bin_path = vpp_bin_path
1063 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001064
1065
Damjan Marionf56b77a2016-10-03 19:44:57 +02001066class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001067 """
1068 @property result_string
1069 String variable to store the test case result string.
1070 @property errors
1071 List variable containing 2-tuples of TestCase instances and strings
1072 holding formatted tracebacks. Each tuple represents a test which
1073 raised an unexpected exception.
1074 @property failures
1075 List variable containing 2-tuples of TestCase instances and strings
1076 holding formatted tracebacks. Each tuple represents a test where
1077 a failure was explicitly signalled using the TestCase.assert*()
1078 methods.
1079 """
1080
juraj.linkes40dd73b2018-09-21 13:55:16 +02001081 failed_test_cases_info = set()
1082 core_crash_test_cases_info = set()
1083 current_test_case_info = None
1084
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001085 def __init__(self, stream=None, descriptions=None, verbosity=None,
1086 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001087 """
Klement Sekerada505f62017-01-04 12:58:53 +01001088 :param stream File descriptor to store where to report test results.
1089 Set to the standard error stream by default.
1090 :param descriptions Boolean variable to store information if to use
1091 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001092 :param verbosity Integer variable to store required verbosity level.
1093 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001094 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001095 self.stream = stream
1096 self.descriptions = descriptions
1097 self.verbosity = verbosity
1098 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001099 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001100
Damjan Marionf56b77a2016-10-03 19:44:57 +02001101 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001102 """
1103 Record a test succeeded result
1104
1105 :param test:
1106
1107 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001108 if self.current_test_case_info:
1109 self.current_test_case_info.logger.debug(
1110 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1111 test._testMethodName,
1112 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001113 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001114 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001115
juraj.linkescae64f82018-09-19 15:01:47 +02001116 self.send_result_through_pipe(test, PASS)
1117
Klement Sekeraf62ae122016-10-11 11:47:09 +02001118 def addSkip(self, test, reason):
1119 """
1120 Record a test skipped.
1121
1122 :param test:
1123 :param reason:
1124
1125 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001126 if self.current_test_case_info:
1127 self.current_test_case_info.logger.debug(
1128 "--- addSkip() %s.%s(%s) called, reason is %s" %
1129 (test.__class__.__name__, test._testMethodName,
1130 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001131 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001132 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001133
juraj.linkescae64f82018-09-19 15:01:47 +02001134 self.send_result_through_pipe(test, SKIP)
1135
juraj.linkes40dd73b2018-09-21 13:55:16 +02001136 def symlink_failed(self):
1137 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001138 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001139 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001140 link_path = os.path.join(
1141 failed_dir,
1142 '%s-FAILED' %
1143 os.path.basename(self.current_test_case_info.tempdir))
1144 if self.current_test_case_info.logger:
1145 self.current_test_case_info.logger.debug(
1146 "creating a link to the failed test")
1147 self.current_test_case_info.logger.debug(
1148 "os.symlink(%s, %s)" %
1149 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001150 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001151 if self.current_test_case_info.logger:
1152 self.current_test_case_info.logger.debug(
1153 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001154 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001155 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001156
Klement Sekeraf413bef2017-08-15 07:09:02 +02001157 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001158 if self.current_test_case_info.logger:
1159 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001160
juraj.linkescae64f82018-09-19 15:01:47 +02001161 def send_result_through_pipe(self, test, result):
1162 if hasattr(self, 'test_framework_result_pipe'):
1163 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001164 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001165 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001166
juraj.linkes40dd73b2018-09-21 13:55:16 +02001167 def log_error(self, test, err, fn_name):
1168 if self.current_test_case_info:
1169 if isinstance(test, unittest.suite._ErrorHolder):
1170 test_name = test.description
1171 else:
1172 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1173 test._testMethodName,
1174 test._testMethodDoc)
1175 self.current_test_case_info.logger.debug(
1176 "--- %s() %s called, err is %s" %
1177 (fn_name, test_name, err))
1178 self.current_test_case_info.logger.debug(
1179 "formatted exception is:\n%s" %
1180 "".join(format_exception(*err)))
1181
1182 def add_error(self, test, err, unittest_fn, error_type):
1183 if error_type == FAIL:
1184 self.log_error(test, err, 'addFailure')
1185 error_type_str = colorize("FAIL", RED)
1186 elif error_type == ERROR:
1187 self.log_error(test, err, 'addError')
1188 error_type_str = colorize("ERROR", RED)
1189 else:
1190 raise Exception('Error type %s cannot be used to record an '
1191 'error or a failure' % error_type)
1192
1193 unittest_fn(self, test, err)
1194 if self.current_test_case_info:
1195 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1196 (error_type_str,
1197 self.current_test_case_info.tempdir)
1198 self.symlink_failed()
1199 self.failed_test_cases_info.add(self.current_test_case_info)
1200 if is_core_present(self.current_test_case_info.tempdir):
1201 if not self.current_test_case_info.core_crash_test:
1202 if isinstance(test, unittest.suite._ErrorHolder):
1203 test_name = str(test)
1204 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001205 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001206 get_testcase_doc_name(test), test.id())
1207 self.current_test_case_info.core_crash_test = test_name
1208 self.core_crash_test_cases_info.add(
1209 self.current_test_case_info)
1210 else:
1211 self.result_string = '%s [no temp dir]' % error_type_str
1212
1213 self.send_result_through_pipe(test, error_type)
1214
Damjan Marionf56b77a2016-10-03 19:44:57 +02001215 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001216 """
1217 Record a test failed result
1218
1219 :param test:
1220 :param err: error message
1221
1222 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001223 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001224
Damjan Marionf56b77a2016-10-03 19:44:57 +02001225 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001226 """
1227 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001228
Klement Sekeraf62ae122016-10-11 11:47:09 +02001229 :param test:
1230 :param err: error message
1231
1232 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001233 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001234
Damjan Marionf56b77a2016-10-03 19:44:57 +02001235 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001236 """
1237 Get test description
1238
1239 :param test:
1240 :returns: test description
1241
1242 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001243 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001244
Damjan Marionf56b77a2016-10-03 19:44:57 +02001245 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001246 """
1247 Start a test
1248
1249 :param test:
1250
1251 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001252
1253 def print_header(test):
1254 if not hasattr(test.__class__, '_header_printed'):
1255 print(double_line_delim)
1256 print(colorize(getdoc(test).splitlines()[0], GREEN))
1257 print(double_line_delim)
1258 test.__class__._header_printed = True
1259
1260 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001261
Damjan Marionf56b77a2016-10-03 19:44:57 +02001262 unittest.TestResult.startTest(self, test)
1263 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001264 self.stream.writeln(
1265 "Starting " + self.getDescription(test) + " ...")
1266 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001267
Damjan Marionf56b77a2016-10-03 19:44:57 +02001268 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001269 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001270 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001271
1272 :param test:
1273
1274 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001275 unittest.TestResult.stopTest(self, test)
1276 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001277 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001278 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001279 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001280 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001281 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001282 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001283 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001284
1285 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001286
Damjan Marionf56b77a2016-10-03 19:44:57 +02001287 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001288 """
1289 Print errors from running the test case
1290 """
juraj.linkesabec0122018-11-16 17:28:56 +01001291 if len(self.errors) > 0 or len(self.failures) > 0:
1292 self.stream.writeln()
1293 self.printErrorList('ERROR', self.errors)
1294 self.printErrorList('FAIL', self.failures)
1295
1296 # ^^ that is the last output from unittest before summary
1297 if not self.runner.print_summary:
1298 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1299 self.stream = devnull
1300 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001301
Damjan Marionf56b77a2016-10-03 19:44:57 +02001302 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001303 """
1304 Print error list to the output stream together with error type
1305 and test case description.
1306
1307 :param flavour: error type
1308 :param errors: iterable errors
1309
1310 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001311 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001312 self.stream.writeln(double_line_delim)
1313 self.stream.writeln("%s: %s" %
1314 (flavour, self.getDescription(test)))
1315 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001316 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001317
1318
Damjan Marionf56b77a2016-10-03 19:44:57 +02001319class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001320 """
Klement Sekera104543f2017-02-03 07:29:43 +01001321 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001322 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001323
Klement Sekeraf62ae122016-10-11 11:47:09 +02001324 @property
1325 def resultclass(self):
1326 """Class maintaining the results of the tests"""
1327 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001328
juraj.linkes184870a2018-07-16 14:22:01 +02001329 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001330 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001331 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001332 # ignore stream setting here, use hard-coded stdout to be in sync
1333 # with prints from VppTestCase methods ...
1334 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1335 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001336 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001337 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001338
juraj.linkesabec0122018-11-16 17:28:56 +01001339 self.orig_stream = self.stream
1340 self.resultclass.test_framework_result_pipe = result_pipe
1341
1342 self.print_summary = print_summary
1343
1344 def _makeResult(self):
1345 return self.resultclass(self.stream,
1346 self.descriptions,
1347 self.verbosity,
1348 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001349
Damjan Marionf56b77a2016-10-03 19:44:57 +02001350 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001351 """
1352 Run the tests
1353
1354 :param test:
1355
1356 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001357 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001358
1359 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001360 if not self.print_summary:
1361 self.stream = self.orig_stream
1362 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001363 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001364
1365
1366class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001367 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001368 self.logger = logger
1369 self.args = args
1370 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001371 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001372 super(Worker, self).__init__()
1373
1374 def run(self):
1375 executable = self.args[0]
1376 self.logger.debug("Running executable w/args `%s'" % self.args)
1377 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001378 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001379 env["CK_LOG_FILE_NAME"] = "-"
1380 self.process = subprocess.Popen(
1381 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1382 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1383 out, err = self.process.communicate()
1384 self.logger.debug("Finished running `%s'" % executable)
1385 self.logger.info("Return code is `%s'" % self.process.returncode)
1386 self.logger.info(single_line_delim)
1387 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1388 self.logger.info(single_line_delim)
1389 self.logger.info(out)
1390 self.logger.info(single_line_delim)
1391 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1392 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001393 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001394 self.logger.info(single_line_delim)
1395 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001396
1397if __name__ == '__main__':
1398 pass