blob: 25db2b72b3403cba576530e39a0f04d63c052a9e [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", "{",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200316 "main-core", str(cpu_core_number), "}",
317 "statseg", "{", "socket-name", cls.stats_sock, "}",
318 "socksvr", "{", "socket-name", cls.api_sock, "}",
319 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100320 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200321 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100322 "}", "plugin", "unittest_plugin.so", "{", "enable",
323 "}"] + cls.extra_vpp_plugin_config + ["}", ]
324 if cls.extra_vpp_punt_config is not None:
325 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100326 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100327 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100328 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
329 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200330
331 @classmethod
332 def wait_for_enter(cls):
333 if cls.debug_gdbserver:
334 print(double_line_delim)
335 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
336 elif cls.debug_gdb:
337 print(double_line_delim)
338 print("Spawned VPP with PID: %d" % cls.vpp.pid)
339 else:
340 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
341 return
342 print(single_line_delim)
343 print("You can debug the VPP using e.g.:")
344 if cls.debug_gdbserver:
345 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
346 print("Now is the time to attach a gdb by running the above "
347 "command, set up breakpoints etc. and then resume VPP from "
348 "within gdb by issuing the 'continue' command")
349 elif cls.debug_gdb:
350 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
351 print("Now is the time to attach a gdb by running the above "
352 "command and set up breakpoints etc.")
353 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800354 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200355
356 @classmethod
357 def run_vpp(cls):
358 cmdline = cls.vpp_cmdline
359
360 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100361 gdbserver = '/usr/bin/gdbserver'
362 if not os.path.isfile(gdbserver) or \
363 not os.access(gdbserver, os.X_OK):
364 raise Exception("gdbserver binary '%s' does not exist or is "
365 "not executable" % gdbserver)
366
367 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200368 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
369
Klement Sekera931be3a2016-11-03 05:36:01 +0100370 try:
371 cls.vpp = subprocess.Popen(cmdline,
372 stdout=subprocess.PIPE,
373 stderr=subprocess.PIPE,
374 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800375 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800376 cls.logger.critical("Subprocess returned with non-0 return code: ("
377 "%s)", e.returncode)
378 raise
379 except OSError as e:
380 cls.logger.critical("Subprocess returned with OS error: "
381 "(%s) %s", e.errno, e.strerror)
382 raise
383 except Exception as e:
384 cls.logger.exception("Subprocess returned unexpected from "
385 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100386 raise
387
Klement Sekera277b89c2016-10-28 13:20:27 +0200388 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100389
Damjan Marionf56b77a2016-10-03 19:44:57 +0200390 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200391 def wait_for_stats_socket(cls):
392 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800393 ok = False
394 while time.time() < deadline or \
395 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200396 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800397 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200398 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700399 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800400 if not ok:
401 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200402
403 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200404 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200405 """
406 Perform class setup before running the testcase
407 Remove shared memory files, start vpp and connect the vpp-api
408 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800409 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100410 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100411 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100412 cls.logger = get_logger(cls.__name__)
413 if hasattr(cls, 'parallel_handler'):
414 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100415 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700416
Klement Sekeraf62ae122016-10-11 11:47:09 +0200417 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200418 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200419 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200420 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200421 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
422 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100423 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
424 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200425 cls.file_handler.setLevel(DEBUG)
426 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700427 cls.logger.debug("--- setUpClass() for %s called ---" %
428 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200429 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200430 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200431 cls.logger.info("Temporary dir is %s, shm prefix is %s",
432 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200433 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100434 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100435 cls._captures = []
436 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200437 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100438 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100439 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200440 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200441 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200442 # need to catch exceptions here because if we raise, then the cleanup
443 # doesn't get called and we might end with a zombie vpp
444 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200445 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200446 cls.reporter.send_keep_alive(cls, 'setUpClass')
447 VppTestResult.current_test_case_info = TestCaseInfo(
448 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100449 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100450 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100451 cls.pump_thread_stop_flag = Event()
452 cls.pump_thread_wakeup_pipe = os.pipe()
453 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100454 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100455 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200456 if cls.debug_gdb or cls.debug_gdbserver:
457 read_timeout = 0
458 else:
459 read_timeout = 5
460 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
461 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100462 if cls.step:
463 hook = StepHook(cls)
464 else:
465 hook = PollHook(cls)
466 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200467 cls.wait_for_stats_socket()
468 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200469 try:
470 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100471 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200472 cls.vpp_startup_failed = True
473 cls.logger.critical(
474 "VPP died shortly after startup, check the"
475 " output to standard error for possible cause")
476 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100477 try:
478 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100479 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100480 try:
481 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100482 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100483 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100484 if cls.debug_gdbserver:
485 print(colorize("You're running VPP inside gdbserver but "
486 "VPP-API connection failed, did you forget "
487 "to 'continue' VPP from within gdb?", RED))
488 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100489 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100490 try:
491 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100492 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100493 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100494 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200495
Damjan Marionf56b77a2016-10-03 19:44:57 +0200496 @classmethod
497 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200498 """
499 Disconnect vpp-api, kill vpp and cleanup shared memory files
500 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200501 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
502 cls.vpp.poll()
503 if cls.vpp.returncode is None:
504 print(double_line_delim)
505 print("VPP or GDB server is still running")
506 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800507 input("When done debugging, press ENTER to kill the "
508 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200509
juraj.linkes184870a2018-07-16 14:22:01 +0200510 # first signal that we want to stop the pump thread, then wake it up
511 if hasattr(cls, 'pump_thread_stop_flag'):
512 cls.pump_thread_stop_flag.set()
513 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100514 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100515 if hasattr(cls, 'pump_thread'):
516 cls.logger.debug("Waiting for pump thread to stop")
517 cls.pump_thread.join()
518 if hasattr(cls, 'vpp_stderr_reader_thread'):
519 cls.logger.debug("Waiting for stdderr pump to stop")
520 cls.vpp_stderr_reader_thread.join()
521
Klement Sekeraf62ae122016-10-11 11:47:09 +0200522 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100523 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700524 cls.logger.debug("Disconnecting class vapi client on %s",
525 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100526 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700527 cls.logger.debug("Deleting class vapi attribute on %s",
528 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100529 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200530 cls.vpp.poll()
531 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100532 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200533 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100534 cls.logger.debug("Waiting for vpp to die")
535 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700536 cls.logger.debug("Deleting class vpp attribute on %s",
537 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200538 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200539
Klement Sekera3747c752017-04-10 06:30:17 +0200540 if cls.vpp_startup_failed:
541 stdout_log = cls.logger.info
542 stderr_log = cls.logger.critical
543 else:
544 stdout_log = cls.logger.info
545 stderr_log = cls.logger.info
546
Klement Sekerae4504c62016-12-08 10:16:41 +0100547 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200548 stdout_log(single_line_delim)
549 stdout_log('VPP output to stdout while running %s:', cls.__name__)
550 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100551 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200552 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
553 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200554 stdout_log('\n%s', vpp_output)
555 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200556
Klement Sekerae4504c62016-12-08 10:16:41 +0100557 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200558 stderr_log(single_line_delim)
559 stderr_log('VPP output to stderr while running %s:', cls.__name__)
560 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100561 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200562 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
563 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200564 stderr_log('\n%s', vpp_output)
565 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200566
Damjan Marionf56b77a2016-10-03 19:44:57 +0200567 @classmethod
568 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200569 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700570 cls.logger.debug("--- tearDownClass() for %s called ---" %
571 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200572 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200573 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200574 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100575 cls.reset_packet_infos()
576 if debug_framework:
577 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200578
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700579 def show_commands_at_teardown(self):
580 """ Allow subclass specific teardown logging additions."""
581 self.logger.info("--- No test specific show commands provided. ---")
582
Damjan Marionf56b77a2016-10-03 19:44:57 +0200583 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200584 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100585 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
586 (self.__class__.__name__, self._testMethodName,
587 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200588 if not self.vpp_dead:
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700589 self.logger.info(
590 "--- Logging show commands common to all testcases. ---")
Klement Sekerad91fa612019-01-15 13:25:09 +0100591 self.logger.debug(self.vapi.cli("show trace max 1000"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700592 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200593 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200594 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200595 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800596 self.logger.info(self.vapi.ppcli("show log"))
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700597 self.logger.info("Logging testcase specific show commands.")
598 self.show_commands_at_teardown()
Klement Sekera10db26f2017-01-11 08:16:53 +0100599 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500600 # Save/Dump VPP api trace log
601 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
602 tmp_api_trace = "/tmp/%s" % api_trace
603 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
604 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
605 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
606 vpp_api_trace_log))
607 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500608 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500609 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100610 else:
611 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200612
Damjan Marionf56b77a2016-10-03 19:44:57 +0200613 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200614 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800615 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200616 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100617 if self.vpp_dead:
618 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100619 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100620 self.vpp_stdout_deque.append(
621 "--- test setUp() for %s.%s(%s) starts here ---\n" %
622 (self.__class__.__name__, self._testMethodName,
623 self._testMethodDoc))
624 self.vpp_stderr_deque.append(
625 "--- test setUp() for %s.%s(%s) starts here ---\n" %
626 (self.__class__.__name__, self._testMethodName,
627 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200628 self.vapi.cli("clear trace")
629 # store the test instance inside the test class - so that objects
630 # holding the class can access instance methods (like assertEqual)
631 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200632
Damjan Marionf56b77a2016-10-03 19:44:57 +0200633 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200634 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200635 """
636 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200637
Klement Sekera75e7d132017-09-20 08:26:30 +0200638 :param interfaces: iterable interface indexes (if None,
639 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200640
Klement Sekeraf62ae122016-10-11 11:47:09 +0200641 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200642 if interfaces is None:
643 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200644 for i in interfaces:
645 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200646
Damjan Marionf56b77a2016-10-03 19:44:57 +0200647 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100648 def register_capture(cls, cap_name):
649 """ Register a capture in the testclass """
650 # add to the list of captures with current timestamp
651 cls._captures.append((time.time(), cap_name))
652 # filter out from zombies
653 cls._zombie_captures = [(stamp, name)
654 for (stamp, name) in cls._zombie_captures
655 if name != cap_name]
656
657 @classmethod
658 def pg_start(cls):
659 """ Remove any zombie captures and enable the packet generator """
660 # how long before capture is allowed to be deleted - otherwise vpp
661 # crashes - 100ms seems enough (this shouldn't be needed at all)
662 capture_ttl = 0.1
663 now = time.time()
664 for stamp, cap_name in cls._zombie_captures:
665 wait = stamp + capture_ttl - now
666 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100667 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100668 now = time.time()
669 cls.logger.debug("Removing zombie capture %s" % cap_name)
670 cls.vapi.cli('packet-generator delete %s' % cap_name)
671
Klement Sekerad91fa612019-01-15 13:25:09 +0100672 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200673 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100674 cls._zombie_captures = cls._captures
675 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200676
Damjan Marionf56b77a2016-10-03 19:44:57 +0200677 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200678 def create_pg_interfaces(cls, interfaces):
679 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100680 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200681
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100682 :param interfaces: iterable indexes of the interfaces.
683 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200684
Klement Sekeraf62ae122016-10-11 11:47:09 +0200685 """
686 result = []
687 for i in interfaces:
688 intf = VppPGInterface(cls, i)
689 setattr(cls, intf.name, intf)
690 result.append(intf)
691 cls.pg_interfaces = result
692 return result
693
Matej Klotton0178d522016-11-04 11:11:44 +0100694 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200695 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100696 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100697 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100698
Klement Sekerab9ef2732018-06-24 22:49:33 +0200699 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100700 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100701 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200702 result = [VppLoInterface(cls) for i in range(count)]
703 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100704 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100705 cls.lo_interfaces = result
706 return result
707
Neale Ranns192b13f2019-03-15 02:16:20 -0700708 @classmethod
709 def create_bvi_interfaces(cls, count):
710 """
711 Create BVI interfaces.
712
713 :param count: number of interfaces created.
714 :returns: List of created interfaces.
715 """
716 result = [VppBviInterface(cls) for i in range(count)]
717 for intf in result:
718 setattr(cls, intf.name, intf)
719 cls.bvi_interfaces = result
720 return result
721
Damjan Marionf56b77a2016-10-03 19:44:57 +0200722 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200723 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200724 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200725 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200726 NOTE: Currently works only when Raw layer is present.
727
728 :param packet: packet
729 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200730 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200731
732 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200733 packet_len = len(packet) + 4
734 extend = size - packet_len
735 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200736 num = (extend / len(padding)) + 1
737 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200738
Klement Sekeradab231a2016-12-21 08:50:14 +0100739 @classmethod
740 def reset_packet_infos(cls):
741 """ Reset the list of packet info objects and packet counts to zero """
742 cls._packet_infos = {}
743 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200744
Klement Sekeradab231a2016-12-21 08:50:14 +0100745 @classmethod
746 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200747 """
748 Create packet info object containing the source and destination indexes
749 and add it to the testcase's packet info list
750
Klement Sekeradab231a2016-12-21 08:50:14 +0100751 :param VppInterface src_if: source interface
752 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200753
754 :returns: _PacketInfo object
755
756 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200757 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100758 info.index = len(cls._packet_infos)
759 info.src = src_if.sw_if_index
760 info.dst = dst_if.sw_if_index
761 if isinstance(dst_if, VppSubInterface):
762 dst_idx = dst_if.parent.sw_if_index
763 else:
764 dst_idx = dst_if.sw_if_index
765 if dst_idx in cls._packet_count_for_dst_if_idx:
766 cls._packet_count_for_dst_if_idx[dst_idx] += 1
767 else:
768 cls._packet_count_for_dst_if_idx[dst_idx] = 1
769 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200770 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200771
Damjan Marionf56b77a2016-10-03 19:44:57 +0200772 @staticmethod
773 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200774 """
775 Convert _PacketInfo object to packet payload
776
777 :param info: _PacketInfo object
778
779 :returns: string containing serialized data from packet info
780 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100781 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
782 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200783
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800785 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200786 """
787 Convert packet payload to _PacketInfo object
788
789 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700790 :type payload: <class 'scapy.packet.Raw'>
791 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800792 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700793 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200794 :returns: _PacketInfo object containing de-serialized data from payload
795
796 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800797 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200798 info = _PacketInfo()
799 info.index = int(numbers[0])
800 info.src = int(numbers[1])
801 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100802 info.ip = int(numbers[3])
803 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200804 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200805
Damjan Marionf56b77a2016-10-03 19:44:57 +0200806 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200807 """
808 Iterate over the packet info list stored in the testcase
809 Start iteration with first element if info is None
810 Continue based on index in info if info is specified
811
812 :param info: info or None
813 :returns: next info in list or None if no more infos
814 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200815 if info is None:
816 next_index = 0
817 else:
818 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100819 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200820 return None
821 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100822 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200823
Klement Sekeraf62ae122016-10-11 11:47:09 +0200824 def get_next_packet_info_for_interface(self, src_index, info):
825 """
826 Search the packet info list for the next packet info with same source
827 interface index
828
829 :param src_index: source interface index to search for
830 :param info: packet info - where to start the search
831 :returns: packet info or None
832
833 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200834 while True:
835 info = self.get_next_packet_info(info)
836 if info is None:
837 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200838 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200839 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200840
Klement Sekeraf62ae122016-10-11 11:47:09 +0200841 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
842 """
843 Search the packet info list for the next packet info with same source
844 and destination interface indexes
845
846 :param src_index: source interface index to search for
847 :param dst_index: destination interface index to search for
848 :param info: packet info - where to start the search
849 :returns: packet info or None
850
851 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200852 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200853 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200854 if info is None:
855 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200856 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200857 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200858
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200859 def assert_equal(self, real_value, expected_value, name_or_class=None):
860 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100861 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200862 return
863 try:
864 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
865 msg = msg % (getdoc(name_or_class).strip(),
866 real_value, str(name_or_class(real_value)),
867 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100868 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200869 msg = "Invalid %s: %s does not match expected value %s" % (
870 name_or_class, real_value, expected_value)
871
872 self.assertEqual(real_value, expected_value, msg)
873
Klement Sekerab17dd962017-01-09 07:43:48 +0100874 def assert_in_range(self,
875 real_value,
876 expected_min,
877 expected_max,
878 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200879 if name is None:
880 msg = None
881 else:
882 msg = "Invalid %s: %s out of range <%s,%s>" % (
883 name, real_value, expected_min, expected_max)
884 self.assertTrue(expected_min <= real_value <= expected_max, msg)
885
Klement Sekerad81ae412018-05-16 10:52:54 +0200886 def assert_packet_checksums_valid(self, packet,
887 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700888 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200889 self.logger.debug(
890 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200891 udp_layers = ['UDP', 'UDPerror']
892 checksum_fields = ['cksum', 'chksum']
893 checksums = []
894 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700895 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200896 while True:
897 layer = temp.getlayer(counter)
898 if layer:
899 for cf in checksum_fields:
900 if hasattr(layer, cf):
901 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100902 0 == getattr(layer, cf) and \
903 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200904 continue
905 delattr(layer, cf)
906 checksums.append((counter, cf))
907 else:
908 break
909 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200910 if 0 == len(checksums):
911 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700912 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200913 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200914 calc_sum = getattr(temp[layer], cf)
915 self.assert_equal(
916 getattr(received[layer], cf), calc_sum,
917 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
918 self.logger.debug(
919 "Checksum field `%s` on `%s` layer has correct value `%s`" %
920 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200921
922 def assert_checksum_valid(self, received_packet, layer,
923 field_name='chksum',
924 ignore_zero_checksum=False):
925 """ Check checksum of received packet on given layer """
926 received_packet_checksum = getattr(received_packet[layer], field_name)
927 if ignore_zero_checksum and 0 == received_packet_checksum:
928 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700929 recalculated = received_packet.__class__(
930 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200931 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700932 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +0200933 self.assert_equal(received_packet_checksum,
934 getattr(recalculated[layer], field_name),
935 "packet checksum on layer: %s" % layer)
936
937 def assert_ip_checksum_valid(self, received_packet,
938 ignore_zero_checksum=False):
939 self.assert_checksum_valid(received_packet, 'IP',
940 ignore_zero_checksum=ignore_zero_checksum)
941
942 def assert_tcp_checksum_valid(self, received_packet,
943 ignore_zero_checksum=False):
944 self.assert_checksum_valid(received_packet, 'TCP',
945 ignore_zero_checksum=ignore_zero_checksum)
946
947 def assert_udp_checksum_valid(self, received_packet,
948 ignore_zero_checksum=True):
949 self.assert_checksum_valid(received_packet, 'UDP',
950 ignore_zero_checksum=ignore_zero_checksum)
951
952 def assert_embedded_icmp_checksum_valid(self, received_packet):
953 if received_packet.haslayer(IPerror):
954 self.assert_checksum_valid(received_packet, 'IPerror')
955 if received_packet.haslayer(TCPerror):
956 self.assert_checksum_valid(received_packet, 'TCPerror')
957 if received_packet.haslayer(UDPerror):
958 self.assert_checksum_valid(received_packet, 'UDPerror',
959 ignore_zero_checksum=True)
960 if received_packet.haslayer(ICMPerror):
961 self.assert_checksum_valid(received_packet, 'ICMPerror')
962
963 def assert_icmp_checksum_valid(self, received_packet):
964 self.assert_checksum_valid(received_packet, 'ICMP')
965 self.assert_embedded_icmp_checksum_valid(received_packet)
966
967 def assert_icmpv6_checksum_valid(self, pkt):
968 if pkt.haslayer(ICMPv6DestUnreach):
969 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
970 self.assert_embedded_icmp_checksum_valid(pkt)
971 if pkt.haslayer(ICMPv6EchoRequest):
972 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
973 if pkt.haslayer(ICMPv6EchoReply):
974 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
975
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100976 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +0100977 if counter.startswith("/"):
978 counter_value = self.statistics.get_counter(counter)
979 self.assert_equal(counter_value, expected_value,
980 "packet counter `%s'" % counter)
981 else:
982 counters = self.vapi.cli("sh errors").split('\n')
983 counter_value = -1
984 for i in range(1, len(counters) - 1):
985 results = counters[i].split()
986 if results[1] == counter:
987 counter_value = int(results[0])
988 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100989
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100990 @classmethod
991 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700992
993 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
994 # * by Guido, only the main thread can be interrupted.
995 # */
996 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
997 if timeout == 0:
998 # yield quantum
999 if hasattr(os, 'sched_yield'):
1000 os.sched_yield()
1001 else:
1002 time.sleep(0)
1003 return
1004
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001005 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001006 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001007 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001008 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001009 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001010 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001011 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001012 "slept for %es instead of ~%es!",
1013 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001014 if hasattr(cls, 'logger'):
1015 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001016 "Finished sleep (%s) - slept %es (wanted %es)",
1017 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001018
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001019 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001020 self.vapi.cli("clear trace")
1021 intf.add_stream(pkts)
1022 self.pg_enable_capture(self.pg_interfaces)
1023 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001024
1025 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1026 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001027 if not timeout:
1028 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001029 for i in self.pg_interfaces:
1030 i.get_capture(0, timeout=timeout)
1031 i.assert_nothing_captured(remark=remark)
1032 timeout = 0.1
1033
Neale Rannsd7603d92019-03-28 08:56:10 +00001034 def send_and_expect(self, intf, pkts, output, n_rx=None):
1035 if not n_rx:
1036 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001037 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001038 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001039 return rx
1040
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001041 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1042 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001043 rx = output.get_capture(len(pkts))
1044 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001045 if not timeout:
1046 timeout = 1
1047 for i in self.pg_interfaces:
1048 if i not in outputs:
1049 i.get_capture(0, timeout=timeout)
1050 i.assert_nothing_captured()
1051 timeout = 0.1
1052
Neale Ranns52fae862018-01-08 04:41:42 -08001053 return rx
1054
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001055 def runTest(self):
1056 """ unittest calls runTest when TestCase is instantiated without a
1057 test case. Use case: Writing unittests against VppTestCase"""
1058 pass
1059
Damjan Marionf56b77a2016-10-03 19:44:57 +02001060
juraj.linkes184870a2018-07-16 14:22:01 +02001061def get_testcase_doc_name(test):
1062 return getdoc(test.__class__).splitlines()[0]
1063
1064
Ole Trøan5ba91592018-11-22 10:01:09 +00001065def get_test_description(descriptions, test):
1066 short_description = test.shortDescription()
1067 if descriptions and short_description:
1068 return short_description
1069 else:
1070 return str(test)
1071
1072
juraj.linkes40dd73b2018-09-21 13:55:16 +02001073class TestCaseInfo(object):
1074 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1075 self.logger = logger
1076 self.tempdir = tempdir
1077 self.vpp_pid = vpp_pid
1078 self.vpp_bin_path = vpp_bin_path
1079 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001080
1081
Damjan Marionf56b77a2016-10-03 19:44:57 +02001082class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001083 """
1084 @property result_string
1085 String variable to store the test case result string.
1086 @property errors
1087 List variable containing 2-tuples of TestCase instances and strings
1088 holding formatted tracebacks. Each tuple represents a test which
1089 raised an unexpected exception.
1090 @property failures
1091 List variable containing 2-tuples of TestCase instances and strings
1092 holding formatted tracebacks. Each tuple represents a test where
1093 a failure was explicitly signalled using the TestCase.assert*()
1094 methods.
1095 """
1096
juraj.linkes40dd73b2018-09-21 13:55:16 +02001097 failed_test_cases_info = set()
1098 core_crash_test_cases_info = set()
1099 current_test_case_info = None
1100
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001101 def __init__(self, stream=None, descriptions=None, verbosity=None,
1102 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001103 """
Klement Sekerada505f62017-01-04 12:58:53 +01001104 :param stream File descriptor to store where to report test results.
1105 Set to the standard error stream by default.
1106 :param descriptions Boolean variable to store information if to use
1107 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001108 :param verbosity Integer variable to store required verbosity level.
1109 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001110 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001111 self.stream = stream
1112 self.descriptions = descriptions
1113 self.verbosity = verbosity
1114 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001115 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001116
Damjan Marionf56b77a2016-10-03 19:44:57 +02001117 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001118 """
1119 Record a test succeeded result
1120
1121 :param test:
1122
1123 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001124 if self.current_test_case_info:
1125 self.current_test_case_info.logger.debug(
1126 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1127 test._testMethodName,
1128 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001129 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001130 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001131
juraj.linkescae64f82018-09-19 15:01:47 +02001132 self.send_result_through_pipe(test, PASS)
1133
Klement Sekeraf62ae122016-10-11 11:47:09 +02001134 def addSkip(self, test, reason):
1135 """
1136 Record a test skipped.
1137
1138 :param test:
1139 :param reason:
1140
1141 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001142 if self.current_test_case_info:
1143 self.current_test_case_info.logger.debug(
1144 "--- addSkip() %s.%s(%s) called, reason is %s" %
1145 (test.__class__.__name__, test._testMethodName,
1146 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001147 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001148 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001149
juraj.linkescae64f82018-09-19 15:01:47 +02001150 self.send_result_through_pipe(test, SKIP)
1151
juraj.linkes40dd73b2018-09-21 13:55:16 +02001152 def symlink_failed(self):
1153 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001154 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001155 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001156 link_path = os.path.join(
1157 failed_dir,
1158 '%s-FAILED' %
1159 os.path.basename(self.current_test_case_info.tempdir))
1160 if self.current_test_case_info.logger:
1161 self.current_test_case_info.logger.debug(
1162 "creating a link to the failed test")
1163 self.current_test_case_info.logger.debug(
1164 "os.symlink(%s, %s)" %
1165 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001166 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001167 if self.current_test_case_info.logger:
1168 self.current_test_case_info.logger.debug(
1169 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001170 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001171 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001172
Klement Sekeraf413bef2017-08-15 07:09:02 +02001173 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001174 if self.current_test_case_info.logger:
1175 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001176
juraj.linkescae64f82018-09-19 15:01:47 +02001177 def send_result_through_pipe(self, test, result):
1178 if hasattr(self, 'test_framework_result_pipe'):
1179 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001180 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001181 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001182
juraj.linkes40dd73b2018-09-21 13:55:16 +02001183 def log_error(self, test, err, fn_name):
1184 if self.current_test_case_info:
1185 if isinstance(test, unittest.suite._ErrorHolder):
1186 test_name = test.description
1187 else:
1188 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1189 test._testMethodName,
1190 test._testMethodDoc)
1191 self.current_test_case_info.logger.debug(
1192 "--- %s() %s called, err is %s" %
1193 (fn_name, test_name, err))
1194 self.current_test_case_info.logger.debug(
1195 "formatted exception is:\n%s" %
1196 "".join(format_exception(*err)))
1197
1198 def add_error(self, test, err, unittest_fn, error_type):
1199 if error_type == FAIL:
1200 self.log_error(test, err, 'addFailure')
1201 error_type_str = colorize("FAIL", RED)
1202 elif error_type == ERROR:
1203 self.log_error(test, err, 'addError')
1204 error_type_str = colorize("ERROR", RED)
1205 else:
1206 raise Exception('Error type %s cannot be used to record an '
1207 'error or a failure' % error_type)
1208
1209 unittest_fn(self, test, err)
1210 if self.current_test_case_info:
1211 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1212 (error_type_str,
1213 self.current_test_case_info.tempdir)
1214 self.symlink_failed()
1215 self.failed_test_cases_info.add(self.current_test_case_info)
1216 if is_core_present(self.current_test_case_info.tempdir):
1217 if not self.current_test_case_info.core_crash_test:
1218 if isinstance(test, unittest.suite._ErrorHolder):
1219 test_name = str(test)
1220 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001221 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001222 get_testcase_doc_name(test), test.id())
1223 self.current_test_case_info.core_crash_test = test_name
1224 self.core_crash_test_cases_info.add(
1225 self.current_test_case_info)
1226 else:
1227 self.result_string = '%s [no temp dir]' % error_type_str
1228
1229 self.send_result_through_pipe(test, error_type)
1230
Damjan Marionf56b77a2016-10-03 19:44:57 +02001231 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001232 """
1233 Record a test failed result
1234
1235 :param test:
1236 :param err: error message
1237
1238 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001239 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001240
Damjan Marionf56b77a2016-10-03 19:44:57 +02001241 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001242 """
1243 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001244
Klement Sekeraf62ae122016-10-11 11:47:09 +02001245 :param test:
1246 :param err: error message
1247
1248 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001249 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001250
Damjan Marionf56b77a2016-10-03 19:44:57 +02001251 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001252 """
1253 Get test description
1254
1255 :param test:
1256 :returns: test description
1257
1258 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001259 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001260
Damjan Marionf56b77a2016-10-03 19:44:57 +02001261 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001262 """
1263 Start a test
1264
1265 :param test:
1266
1267 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001268
1269 def print_header(test):
1270 if not hasattr(test.__class__, '_header_printed'):
1271 print(double_line_delim)
1272 print(colorize(getdoc(test).splitlines()[0], GREEN))
1273 print(double_line_delim)
1274 test.__class__._header_printed = True
1275
1276 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001277
Damjan Marionf56b77a2016-10-03 19:44:57 +02001278 unittest.TestResult.startTest(self, test)
1279 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001280 self.stream.writeln(
1281 "Starting " + self.getDescription(test) + " ...")
1282 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001283
Damjan Marionf56b77a2016-10-03 19:44:57 +02001284 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001285 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001286 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001287
1288 :param test:
1289
1290 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001291 unittest.TestResult.stopTest(self, test)
1292 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001293 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001294 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001295 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001296 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001297 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001298 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001299 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001300
1301 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001302
Damjan Marionf56b77a2016-10-03 19:44:57 +02001303 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001304 """
1305 Print errors from running the test case
1306 """
juraj.linkesabec0122018-11-16 17:28:56 +01001307 if len(self.errors) > 0 or len(self.failures) > 0:
1308 self.stream.writeln()
1309 self.printErrorList('ERROR', self.errors)
1310 self.printErrorList('FAIL', self.failures)
1311
1312 # ^^ that is the last output from unittest before summary
1313 if not self.runner.print_summary:
1314 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1315 self.stream = devnull
1316 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001317
Damjan Marionf56b77a2016-10-03 19:44:57 +02001318 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001319 """
1320 Print error list to the output stream together with error type
1321 and test case description.
1322
1323 :param flavour: error type
1324 :param errors: iterable errors
1325
1326 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001327 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001328 self.stream.writeln(double_line_delim)
1329 self.stream.writeln("%s: %s" %
1330 (flavour, self.getDescription(test)))
1331 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001332 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001333
1334
Damjan Marionf56b77a2016-10-03 19:44:57 +02001335class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001336 """
Klement Sekera104543f2017-02-03 07:29:43 +01001337 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001338 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001339
Klement Sekeraf62ae122016-10-11 11:47:09 +02001340 @property
1341 def resultclass(self):
1342 """Class maintaining the results of the tests"""
1343 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001344
juraj.linkes184870a2018-07-16 14:22:01 +02001345 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001346 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001347 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001348 # ignore stream setting here, use hard-coded stdout to be in sync
1349 # with prints from VppTestCase methods ...
1350 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1351 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001352 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001353 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001354
juraj.linkesabec0122018-11-16 17:28:56 +01001355 self.orig_stream = self.stream
1356 self.resultclass.test_framework_result_pipe = result_pipe
1357
1358 self.print_summary = print_summary
1359
1360 def _makeResult(self):
1361 return self.resultclass(self.stream,
1362 self.descriptions,
1363 self.verbosity,
1364 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001365
Damjan Marionf56b77a2016-10-03 19:44:57 +02001366 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001367 """
1368 Run the tests
1369
1370 :param test:
1371
1372 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001373 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001374
1375 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001376 if not self.print_summary:
1377 self.stream = self.orig_stream
1378 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001379 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001380
1381
1382class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001383 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001384 self.logger = logger
1385 self.args = args
1386 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001387 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001388 super(Worker, self).__init__()
1389
1390 def run(self):
1391 executable = self.args[0]
1392 self.logger.debug("Running executable w/args `%s'" % self.args)
1393 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001394 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001395 env["CK_LOG_FILE_NAME"] = "-"
1396 self.process = subprocess.Popen(
1397 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1398 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1399 out, err = self.process.communicate()
1400 self.logger.debug("Finished running `%s'" % executable)
1401 self.logger.info("Return code is `%s'" % self.process.returncode)
1402 self.logger.info(single_line_delim)
1403 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1404 self.logger.info(single_line_delim)
1405 self.logger.info(out)
1406 self.logger.info(single_line_delim)
1407 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1408 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001409 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001410 self.logger.info(single_line_delim)
1411 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001412
1413if __name__ == '__main__':
1414 pass