blob: 8a92229249bf57d42cb337ce2104649dfa4eb0c5 [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",
Ole Troan2e1c8962019-04-10 09:44:23 +0200319 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100320 "}", "plugin", "unittest_plugin.so", "{", "enable",
321 "}"] + cls.extra_vpp_plugin_config + ["}", ]
322 if cls.extra_vpp_punt_config is not None:
323 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100324 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100325 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100326 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
327 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200328
329 @classmethod
330 def wait_for_enter(cls):
331 if cls.debug_gdbserver:
332 print(double_line_delim)
333 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
334 elif cls.debug_gdb:
335 print(double_line_delim)
336 print("Spawned VPP with PID: %d" % cls.vpp.pid)
337 else:
338 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
339 return
340 print(single_line_delim)
341 print("You can debug the VPP using e.g.:")
342 if cls.debug_gdbserver:
343 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
344 print("Now is the time to attach a gdb by running the above "
345 "command, set up breakpoints etc. and then resume VPP from "
346 "within gdb by issuing the 'continue' command")
347 elif cls.debug_gdb:
348 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
349 print("Now is the time to attach a gdb by running the above "
350 "command and set up breakpoints etc.")
351 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800352 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200353
354 @classmethod
355 def run_vpp(cls):
356 cmdline = cls.vpp_cmdline
357
358 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100359 gdbserver = '/usr/bin/gdbserver'
360 if not os.path.isfile(gdbserver) or \
361 not os.access(gdbserver, os.X_OK):
362 raise Exception("gdbserver binary '%s' does not exist or is "
363 "not executable" % gdbserver)
364
365 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200366 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
367
Klement Sekera931be3a2016-11-03 05:36:01 +0100368 try:
369 cls.vpp = subprocess.Popen(cmdline,
370 stdout=subprocess.PIPE,
371 stderr=subprocess.PIPE,
372 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800373 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800374 cls.logger.critical("Subprocess returned with non-0 return code: ("
375 "%s)", e.returncode)
376 raise
377 except OSError as e:
378 cls.logger.critical("Subprocess returned with OS error: "
379 "(%s) %s", e.errno, e.strerror)
380 raise
381 except Exception as e:
382 cls.logger.exception("Subprocess returned unexpected from "
383 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100384 raise
385
Klement Sekera277b89c2016-10-28 13:20:27 +0200386 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100387
Damjan Marionf56b77a2016-10-03 19:44:57 +0200388 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200389 def wait_for_stats_socket(cls):
390 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800391 ok = False
392 while time.time() < deadline or \
393 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200394 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800395 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200396 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700397 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800398 if not ok:
399 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200400
401 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200402 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200403 """
404 Perform class setup before running the testcase
405 Remove shared memory files, start vpp and connect the vpp-api
406 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800407 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100408 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100409 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100410 cls.logger = get_logger(cls.__name__)
411 if hasattr(cls, 'parallel_handler'):
412 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100413 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700414
Klement Sekeraf62ae122016-10-11 11:47:09 +0200415 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200416 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200417 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200418 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
419 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100420 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
421 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200422 cls.file_handler.setLevel(DEBUG)
423 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700424 cls.logger.debug("--- setUpClass() for %s called ---" %
425 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200426 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200427 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200428 cls.logger.info("Temporary dir is %s, shm prefix is %s",
429 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200430 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100431 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100432 cls._captures = []
433 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200434 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100435 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100436 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200437 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200438 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200439 # need to catch exceptions here because if we raise, then the cleanup
440 # doesn't get called and we might end with a zombie vpp
441 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200442 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200443 cls.reporter.send_keep_alive(cls, 'setUpClass')
444 VppTestResult.current_test_case_info = TestCaseInfo(
445 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100446 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100447 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100448 cls.pump_thread_stop_flag = Event()
449 cls.pump_thread_wakeup_pipe = os.pipe()
450 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100451 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100452 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200453 if cls.debug_gdb or cls.debug_gdbserver:
454 read_timeout = 0
455 else:
456 read_timeout = 5
457 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
458 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100459 if cls.step:
460 hook = StepHook(cls)
461 else:
462 hook = PollHook(cls)
463 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200464 cls.wait_for_stats_socket()
465 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200466 try:
467 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100468 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200469 cls.vpp_startup_failed = True
470 cls.logger.critical(
471 "VPP died shortly after startup, check the"
472 " output to standard error for possible cause")
473 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100474 try:
475 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100476 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100477 try:
478 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100479 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100480 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100481 if cls.debug_gdbserver:
482 print(colorize("You're running VPP inside gdbserver but "
483 "VPP-API connection failed, did you forget "
484 "to 'continue' VPP from within gdb?", RED))
485 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100486 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100487 try:
488 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100489 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100490 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100491 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200492
Damjan Marionf56b77a2016-10-03 19:44:57 +0200493 @classmethod
494 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200495 """
496 Disconnect vpp-api, kill vpp and cleanup shared memory files
497 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200498 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
499 cls.vpp.poll()
500 if cls.vpp.returncode is None:
501 print(double_line_delim)
502 print("VPP or GDB server is still running")
503 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800504 input("When done debugging, press ENTER to kill the "
505 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200506
juraj.linkes184870a2018-07-16 14:22:01 +0200507 # first signal that we want to stop the pump thread, then wake it up
508 if hasattr(cls, 'pump_thread_stop_flag'):
509 cls.pump_thread_stop_flag.set()
510 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100511 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100512 if hasattr(cls, 'pump_thread'):
513 cls.logger.debug("Waiting for pump thread to stop")
514 cls.pump_thread.join()
515 if hasattr(cls, 'vpp_stderr_reader_thread'):
516 cls.logger.debug("Waiting for stdderr pump to stop")
517 cls.vpp_stderr_reader_thread.join()
518
Klement Sekeraf62ae122016-10-11 11:47:09 +0200519 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100520 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700521 cls.logger.debug("Disconnecting class vapi client on %s",
522 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100523 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700524 cls.logger.debug("Deleting class vapi attribute on %s",
525 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100526 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200527 cls.vpp.poll()
528 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100529 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200530 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100531 cls.logger.debug("Waiting for vpp to die")
532 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700533 cls.logger.debug("Deleting class vpp attribute on %s",
534 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200535 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200536
Klement Sekera3747c752017-04-10 06:30:17 +0200537 if cls.vpp_startup_failed:
538 stdout_log = cls.logger.info
539 stderr_log = cls.logger.critical
540 else:
541 stdout_log = cls.logger.info
542 stderr_log = cls.logger.info
543
Klement Sekerae4504c62016-12-08 10:16:41 +0100544 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200545 stdout_log(single_line_delim)
546 stdout_log('VPP output to stdout while running %s:', cls.__name__)
547 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100548 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200549 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
550 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200551 stdout_log('\n%s', vpp_output)
552 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200553
Klement Sekerae4504c62016-12-08 10:16:41 +0100554 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200555 stderr_log(single_line_delim)
556 stderr_log('VPP output to stderr while running %s:', cls.__name__)
557 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100558 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200559 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
560 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200561 stderr_log('\n%s', vpp_output)
562 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200563
Damjan Marionf56b77a2016-10-03 19:44:57 +0200564 @classmethod
565 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200566 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700567 cls.logger.debug("--- tearDownClass() for %s called ---" %
568 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200569 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200570 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200571 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100572 cls.reset_packet_infos()
573 if debug_framework:
574 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200575
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700576 def show_commands_at_teardown(self):
577 """ Allow subclass specific teardown logging additions."""
578 self.logger.info("--- No test specific show commands provided. ---")
579
Damjan Marionf56b77a2016-10-03 19:44:57 +0200580 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200581 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100582 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
583 (self.__class__.__name__, self._testMethodName,
584 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200585 if not self.vpp_dead:
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700586 self.logger.info(
587 "--- Logging show commands common to all testcases. ---")
Klement Sekerad91fa612019-01-15 13:25:09 +0100588 self.logger.debug(self.vapi.cli("show trace max 1000"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700589 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200590 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200591 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200592 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800593 self.logger.info(self.vapi.ppcli("show log"))
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700594 self.logger.info("Logging testcase specific show commands.")
595 self.show_commands_at_teardown()
Klement Sekera10db26f2017-01-11 08:16:53 +0100596 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500597 # Save/Dump VPP api trace log
598 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
599 tmp_api_trace = "/tmp/%s" % api_trace
600 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
601 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
602 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
603 vpp_api_trace_log))
604 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500605 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500606 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100607 else:
608 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200609
Damjan Marionf56b77a2016-10-03 19:44:57 +0200610 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200611 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800612 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200613 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100614 if self.vpp_dead:
615 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100616 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100617 self.vpp_stdout_deque.append(
618 "--- test setUp() for %s.%s(%s) starts here ---\n" %
619 (self.__class__.__name__, self._testMethodName,
620 self._testMethodDoc))
621 self.vpp_stderr_deque.append(
622 "--- test setUp() for %s.%s(%s) starts here ---\n" %
623 (self.__class__.__name__, self._testMethodName,
624 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200625 self.vapi.cli("clear trace")
626 # store the test instance inside the test class - so that objects
627 # holding the class can access instance methods (like assertEqual)
628 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200629
Damjan Marionf56b77a2016-10-03 19:44:57 +0200630 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200631 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200632 """
633 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200634
Klement Sekera75e7d132017-09-20 08:26:30 +0200635 :param interfaces: iterable interface indexes (if None,
636 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200637
Klement Sekeraf62ae122016-10-11 11:47:09 +0200638 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200639 if interfaces is None:
640 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200641 for i in interfaces:
642 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200643
Damjan Marionf56b77a2016-10-03 19:44:57 +0200644 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100645 def register_capture(cls, cap_name):
646 """ Register a capture in the testclass """
647 # add to the list of captures with current timestamp
648 cls._captures.append((time.time(), cap_name))
649 # filter out from zombies
650 cls._zombie_captures = [(stamp, name)
651 for (stamp, name) in cls._zombie_captures
652 if name != cap_name]
653
654 @classmethod
655 def pg_start(cls):
656 """ Remove any zombie captures and enable the packet generator """
657 # how long before capture is allowed to be deleted - otherwise vpp
658 # crashes - 100ms seems enough (this shouldn't be needed at all)
659 capture_ttl = 0.1
660 now = time.time()
661 for stamp, cap_name in cls._zombie_captures:
662 wait = stamp + capture_ttl - now
663 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100664 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100665 now = time.time()
666 cls.logger.debug("Removing zombie capture %s" % cap_name)
667 cls.vapi.cli('packet-generator delete %s' % cap_name)
668
Klement Sekerad91fa612019-01-15 13:25:09 +0100669 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200670 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100671 cls._zombie_captures = cls._captures
672 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200673
Damjan Marionf56b77a2016-10-03 19:44:57 +0200674 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200675 def create_pg_interfaces(cls, interfaces):
676 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100677 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200678
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100679 :param interfaces: iterable indexes of the interfaces.
680 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200681
Klement Sekeraf62ae122016-10-11 11:47:09 +0200682 """
683 result = []
684 for i in interfaces:
685 intf = VppPGInterface(cls, i)
686 setattr(cls, intf.name, intf)
687 result.append(intf)
688 cls.pg_interfaces = result
689 return result
690
Matej Klotton0178d522016-11-04 11:11:44 +0100691 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200692 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100693 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100694 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100695
Klement Sekerab9ef2732018-06-24 22:49:33 +0200696 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100697 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100698 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200699 result = [VppLoInterface(cls) for i in range(count)]
700 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100701 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100702 cls.lo_interfaces = result
703 return result
704
Neale Ranns192b13f2019-03-15 02:16:20 -0700705 @classmethod
706 def create_bvi_interfaces(cls, count):
707 """
708 Create BVI interfaces.
709
710 :param count: number of interfaces created.
711 :returns: List of created interfaces.
712 """
713 result = [VppBviInterface(cls) for i in range(count)]
714 for intf in result:
715 setattr(cls, intf.name, intf)
716 cls.bvi_interfaces = result
717 return result
718
Damjan Marionf56b77a2016-10-03 19:44:57 +0200719 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200720 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200721 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200722 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200723 NOTE: Currently works only when Raw layer is present.
724
725 :param packet: packet
726 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200727 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200728
729 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200730 packet_len = len(packet) + 4
731 extend = size - packet_len
732 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200733 num = (extend / len(padding)) + 1
734 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200735
Klement Sekeradab231a2016-12-21 08:50:14 +0100736 @classmethod
737 def reset_packet_infos(cls):
738 """ Reset the list of packet info objects and packet counts to zero """
739 cls._packet_infos = {}
740 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200741
Klement Sekeradab231a2016-12-21 08:50:14 +0100742 @classmethod
743 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200744 """
745 Create packet info object containing the source and destination indexes
746 and add it to the testcase's packet info list
747
Klement Sekeradab231a2016-12-21 08:50:14 +0100748 :param VppInterface src_if: source interface
749 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200750
751 :returns: _PacketInfo object
752
753 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200754 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100755 info.index = len(cls._packet_infos)
756 info.src = src_if.sw_if_index
757 info.dst = dst_if.sw_if_index
758 if isinstance(dst_if, VppSubInterface):
759 dst_idx = dst_if.parent.sw_if_index
760 else:
761 dst_idx = dst_if.sw_if_index
762 if dst_idx in cls._packet_count_for_dst_if_idx:
763 cls._packet_count_for_dst_if_idx[dst_idx] += 1
764 else:
765 cls._packet_count_for_dst_if_idx[dst_idx] = 1
766 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200767 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200768
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769 @staticmethod
770 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200771 """
772 Convert _PacketInfo object to packet payload
773
774 :param info: _PacketInfo object
775
776 :returns: string containing serialized data from packet info
777 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100778 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
779 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200780
Damjan Marionf56b77a2016-10-03 19:44:57 +0200781 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800782 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200783 """
784 Convert packet payload to _PacketInfo object
785
786 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700787 :type payload: <class 'scapy.packet.Raw'>
788 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800789 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700790 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200791 :returns: _PacketInfo object containing de-serialized data from payload
792
793 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800794 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200795 info = _PacketInfo()
796 info.index = int(numbers[0])
797 info.src = int(numbers[1])
798 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100799 info.ip = int(numbers[3])
800 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200801 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200802
Damjan Marionf56b77a2016-10-03 19:44:57 +0200803 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200804 """
805 Iterate over the packet info list stored in the testcase
806 Start iteration with first element if info is None
807 Continue based on index in info if info is specified
808
809 :param info: info or None
810 :returns: next info in list or None if no more infos
811 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200812 if info is None:
813 next_index = 0
814 else:
815 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100816 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200817 return None
818 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100819 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200820
Klement Sekeraf62ae122016-10-11 11:47:09 +0200821 def get_next_packet_info_for_interface(self, src_index, info):
822 """
823 Search the packet info list for the next packet info with same source
824 interface index
825
826 :param src_index: source interface index to search for
827 :param info: packet info - where to start the search
828 :returns: packet info or None
829
830 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200831 while True:
832 info = self.get_next_packet_info(info)
833 if info is None:
834 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200835 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200836 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200837
Klement Sekeraf62ae122016-10-11 11:47:09 +0200838 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
839 """
840 Search the packet info list for the next packet info with same source
841 and destination interface indexes
842
843 :param src_index: source interface index to search for
844 :param dst_index: destination interface index to search for
845 :param info: packet info - where to start the search
846 :returns: packet info or None
847
848 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200849 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200850 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200851 if info is None:
852 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200853 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200854 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200855
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200856 def assert_equal(self, real_value, expected_value, name_or_class=None):
857 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100858 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200859 return
860 try:
861 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
862 msg = msg % (getdoc(name_or_class).strip(),
863 real_value, str(name_or_class(real_value)),
864 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100865 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200866 msg = "Invalid %s: %s does not match expected value %s" % (
867 name_or_class, real_value, expected_value)
868
869 self.assertEqual(real_value, expected_value, msg)
870
Klement Sekerab17dd962017-01-09 07:43:48 +0100871 def assert_in_range(self,
872 real_value,
873 expected_min,
874 expected_max,
875 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200876 if name is None:
877 msg = None
878 else:
879 msg = "Invalid %s: %s out of range <%s,%s>" % (
880 name, real_value, expected_min, expected_max)
881 self.assertTrue(expected_min <= real_value <= expected_max, msg)
882
Klement Sekerad81ae412018-05-16 10:52:54 +0200883 def assert_packet_checksums_valid(self, packet,
884 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700885 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200886 self.logger.debug(
887 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200888 udp_layers = ['UDP', 'UDPerror']
889 checksum_fields = ['cksum', 'chksum']
890 checksums = []
891 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700892 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200893 while True:
894 layer = temp.getlayer(counter)
895 if layer:
896 for cf in checksum_fields:
897 if hasattr(layer, cf):
898 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100899 0 == getattr(layer, cf) and \
900 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200901 continue
902 delattr(layer, cf)
903 checksums.append((counter, cf))
904 else:
905 break
906 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200907 if 0 == len(checksums):
908 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700909 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200910 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200911 calc_sum = getattr(temp[layer], cf)
912 self.assert_equal(
913 getattr(received[layer], cf), calc_sum,
914 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
915 self.logger.debug(
916 "Checksum field `%s` on `%s` layer has correct value `%s`" %
917 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200918
919 def assert_checksum_valid(self, received_packet, layer,
920 field_name='chksum',
921 ignore_zero_checksum=False):
922 """ Check checksum of received packet on given layer """
923 received_packet_checksum = getattr(received_packet[layer], field_name)
924 if ignore_zero_checksum and 0 == received_packet_checksum:
925 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700926 recalculated = received_packet.__class__(
927 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200928 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700929 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +0200930 self.assert_equal(received_packet_checksum,
931 getattr(recalculated[layer], field_name),
932 "packet checksum on layer: %s" % layer)
933
934 def assert_ip_checksum_valid(self, received_packet,
935 ignore_zero_checksum=False):
936 self.assert_checksum_valid(received_packet, 'IP',
937 ignore_zero_checksum=ignore_zero_checksum)
938
939 def assert_tcp_checksum_valid(self, received_packet,
940 ignore_zero_checksum=False):
941 self.assert_checksum_valid(received_packet, 'TCP',
942 ignore_zero_checksum=ignore_zero_checksum)
943
944 def assert_udp_checksum_valid(self, received_packet,
945 ignore_zero_checksum=True):
946 self.assert_checksum_valid(received_packet, 'UDP',
947 ignore_zero_checksum=ignore_zero_checksum)
948
949 def assert_embedded_icmp_checksum_valid(self, received_packet):
950 if received_packet.haslayer(IPerror):
951 self.assert_checksum_valid(received_packet, 'IPerror')
952 if received_packet.haslayer(TCPerror):
953 self.assert_checksum_valid(received_packet, 'TCPerror')
954 if received_packet.haslayer(UDPerror):
955 self.assert_checksum_valid(received_packet, 'UDPerror',
956 ignore_zero_checksum=True)
957 if received_packet.haslayer(ICMPerror):
958 self.assert_checksum_valid(received_packet, 'ICMPerror')
959
960 def assert_icmp_checksum_valid(self, received_packet):
961 self.assert_checksum_valid(received_packet, 'ICMP')
962 self.assert_embedded_icmp_checksum_valid(received_packet)
963
964 def assert_icmpv6_checksum_valid(self, pkt):
965 if pkt.haslayer(ICMPv6DestUnreach):
966 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
967 self.assert_embedded_icmp_checksum_valid(pkt)
968 if pkt.haslayer(ICMPv6EchoRequest):
969 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
970 if pkt.haslayer(ICMPv6EchoReply):
971 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
972
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100973 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +0100974 if counter.startswith("/"):
975 counter_value = self.statistics.get_counter(counter)
976 self.assert_equal(counter_value, expected_value,
977 "packet counter `%s'" % counter)
978 else:
979 counters = self.vapi.cli("sh errors").split('\n')
980 counter_value = -1
981 for i in range(1, len(counters) - 1):
982 results = counters[i].split()
983 if results[1] == counter:
984 counter_value = int(results[0])
985 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100986
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100987 @classmethod
988 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700989
990 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
991 # * by Guido, only the main thread can be interrupted.
992 # */
993 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
994 if timeout == 0:
995 # yield quantum
996 if hasattr(os, 'sched_yield'):
997 os.sched_yield()
998 else:
999 time.sleep(0)
1000 return
1001
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001002 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001003 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001004 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001005 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001006 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001007 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001008 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001009 "slept for %es instead of ~%es!",
1010 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001011 if hasattr(cls, 'logger'):
1012 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001013 "Finished sleep (%s) - slept %es (wanted %es)",
1014 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001015
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001016 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001017 self.vapi.cli("clear trace")
1018 intf.add_stream(pkts)
1019 self.pg_enable_capture(self.pg_interfaces)
1020 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001021
1022 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1023 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001024 if not timeout:
1025 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001026 for i in self.pg_interfaces:
1027 i.get_capture(0, timeout=timeout)
1028 i.assert_nothing_captured(remark=remark)
1029 timeout = 0.1
1030
Neale Rannsd7603d92019-03-28 08:56:10 +00001031 def send_and_expect(self, intf, pkts, output, n_rx=None):
1032 if not n_rx:
1033 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001034 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001035 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001036 return rx
1037
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001038 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1039 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001040 rx = output.get_capture(len(pkts))
1041 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001042 if not timeout:
1043 timeout = 1
1044 for i in self.pg_interfaces:
1045 if i not in outputs:
1046 i.get_capture(0, timeout=timeout)
1047 i.assert_nothing_captured()
1048 timeout = 0.1
1049
Neale Ranns52fae862018-01-08 04:41:42 -08001050 return rx
1051
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001052 def runTest(self):
1053 """ unittest calls runTest when TestCase is instantiated without a
1054 test case. Use case: Writing unittests against VppTestCase"""
1055 pass
1056
Damjan Marionf56b77a2016-10-03 19:44:57 +02001057
juraj.linkes184870a2018-07-16 14:22:01 +02001058def get_testcase_doc_name(test):
1059 return getdoc(test.__class__).splitlines()[0]
1060
1061
Ole Trøan5ba91592018-11-22 10:01:09 +00001062def get_test_description(descriptions, test):
1063 short_description = test.shortDescription()
1064 if descriptions and short_description:
1065 return short_description
1066 else:
1067 return str(test)
1068
1069
juraj.linkes40dd73b2018-09-21 13:55:16 +02001070class TestCaseInfo(object):
1071 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1072 self.logger = logger
1073 self.tempdir = tempdir
1074 self.vpp_pid = vpp_pid
1075 self.vpp_bin_path = vpp_bin_path
1076 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001077
1078
Damjan Marionf56b77a2016-10-03 19:44:57 +02001079class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001080 """
1081 @property result_string
1082 String variable to store the test case result string.
1083 @property errors
1084 List variable containing 2-tuples of TestCase instances and strings
1085 holding formatted tracebacks. Each tuple represents a test which
1086 raised an unexpected exception.
1087 @property failures
1088 List variable containing 2-tuples of TestCase instances and strings
1089 holding formatted tracebacks. Each tuple represents a test where
1090 a failure was explicitly signalled using the TestCase.assert*()
1091 methods.
1092 """
1093
juraj.linkes40dd73b2018-09-21 13:55:16 +02001094 failed_test_cases_info = set()
1095 core_crash_test_cases_info = set()
1096 current_test_case_info = None
1097
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001098 def __init__(self, stream=None, descriptions=None, verbosity=None,
1099 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001100 """
Klement Sekerada505f62017-01-04 12:58:53 +01001101 :param stream File descriptor to store where to report test results.
1102 Set to the standard error stream by default.
1103 :param descriptions Boolean variable to store information if to use
1104 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001105 :param verbosity Integer variable to store required verbosity level.
1106 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001107 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001108 self.stream = stream
1109 self.descriptions = descriptions
1110 self.verbosity = verbosity
1111 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001112 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001113
Damjan Marionf56b77a2016-10-03 19:44:57 +02001114 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001115 """
1116 Record a test succeeded result
1117
1118 :param test:
1119
1120 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001121 if self.current_test_case_info:
1122 self.current_test_case_info.logger.debug(
1123 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1124 test._testMethodName,
1125 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001126 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001127 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001128
juraj.linkescae64f82018-09-19 15:01:47 +02001129 self.send_result_through_pipe(test, PASS)
1130
Klement Sekeraf62ae122016-10-11 11:47:09 +02001131 def addSkip(self, test, reason):
1132 """
1133 Record a test skipped.
1134
1135 :param test:
1136 :param reason:
1137
1138 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001139 if self.current_test_case_info:
1140 self.current_test_case_info.logger.debug(
1141 "--- addSkip() %s.%s(%s) called, reason is %s" %
1142 (test.__class__.__name__, test._testMethodName,
1143 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001144 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001145 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001146
juraj.linkescae64f82018-09-19 15:01:47 +02001147 self.send_result_through_pipe(test, SKIP)
1148
juraj.linkes40dd73b2018-09-21 13:55:16 +02001149 def symlink_failed(self):
1150 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001151 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001152 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001153 link_path = os.path.join(
1154 failed_dir,
1155 '%s-FAILED' %
1156 os.path.basename(self.current_test_case_info.tempdir))
1157 if self.current_test_case_info.logger:
1158 self.current_test_case_info.logger.debug(
1159 "creating a link to the failed test")
1160 self.current_test_case_info.logger.debug(
1161 "os.symlink(%s, %s)" %
1162 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001163 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001164 if self.current_test_case_info.logger:
1165 self.current_test_case_info.logger.debug(
1166 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001167 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001168 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001169
Klement Sekeraf413bef2017-08-15 07:09:02 +02001170 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001171 if self.current_test_case_info.logger:
1172 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001173
juraj.linkescae64f82018-09-19 15:01:47 +02001174 def send_result_through_pipe(self, test, result):
1175 if hasattr(self, 'test_framework_result_pipe'):
1176 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001177 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001178 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001179
juraj.linkes40dd73b2018-09-21 13:55:16 +02001180 def log_error(self, test, err, fn_name):
1181 if self.current_test_case_info:
1182 if isinstance(test, unittest.suite._ErrorHolder):
1183 test_name = test.description
1184 else:
1185 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1186 test._testMethodName,
1187 test._testMethodDoc)
1188 self.current_test_case_info.logger.debug(
1189 "--- %s() %s called, err is %s" %
1190 (fn_name, test_name, err))
1191 self.current_test_case_info.logger.debug(
1192 "formatted exception is:\n%s" %
1193 "".join(format_exception(*err)))
1194
1195 def add_error(self, test, err, unittest_fn, error_type):
1196 if error_type == FAIL:
1197 self.log_error(test, err, 'addFailure')
1198 error_type_str = colorize("FAIL", RED)
1199 elif error_type == ERROR:
1200 self.log_error(test, err, 'addError')
1201 error_type_str = colorize("ERROR", RED)
1202 else:
1203 raise Exception('Error type %s cannot be used to record an '
1204 'error or a failure' % error_type)
1205
1206 unittest_fn(self, test, err)
1207 if self.current_test_case_info:
1208 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1209 (error_type_str,
1210 self.current_test_case_info.tempdir)
1211 self.symlink_failed()
1212 self.failed_test_cases_info.add(self.current_test_case_info)
1213 if is_core_present(self.current_test_case_info.tempdir):
1214 if not self.current_test_case_info.core_crash_test:
1215 if isinstance(test, unittest.suite._ErrorHolder):
1216 test_name = str(test)
1217 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001218 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001219 get_testcase_doc_name(test), test.id())
1220 self.current_test_case_info.core_crash_test = test_name
1221 self.core_crash_test_cases_info.add(
1222 self.current_test_case_info)
1223 else:
1224 self.result_string = '%s [no temp dir]' % error_type_str
1225
1226 self.send_result_through_pipe(test, error_type)
1227
Damjan Marionf56b77a2016-10-03 19:44:57 +02001228 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001229 """
1230 Record a test failed result
1231
1232 :param test:
1233 :param err: error message
1234
1235 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001236 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001237
Damjan Marionf56b77a2016-10-03 19:44:57 +02001238 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001239 """
1240 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001241
Klement Sekeraf62ae122016-10-11 11:47:09 +02001242 :param test:
1243 :param err: error message
1244
1245 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001246 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001247
Damjan Marionf56b77a2016-10-03 19:44:57 +02001248 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001249 """
1250 Get test description
1251
1252 :param test:
1253 :returns: test description
1254
1255 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001256 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001257
Damjan Marionf56b77a2016-10-03 19:44:57 +02001258 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001259 """
1260 Start a test
1261
1262 :param test:
1263
1264 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001265
1266 def print_header(test):
1267 if not hasattr(test.__class__, '_header_printed'):
1268 print(double_line_delim)
1269 print(colorize(getdoc(test).splitlines()[0], GREEN))
1270 print(double_line_delim)
1271 test.__class__._header_printed = True
1272
1273 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001274
Damjan Marionf56b77a2016-10-03 19:44:57 +02001275 unittest.TestResult.startTest(self, test)
1276 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001277 self.stream.writeln(
1278 "Starting " + self.getDescription(test) + " ...")
1279 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001280
Damjan Marionf56b77a2016-10-03 19:44:57 +02001281 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001282 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001283 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001284
1285 :param test:
1286
1287 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001288 unittest.TestResult.stopTest(self, test)
1289 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001290 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001291 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001292 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001293 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001294 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001295 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001296 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001297
1298 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001299
Damjan Marionf56b77a2016-10-03 19:44:57 +02001300 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001301 """
1302 Print errors from running the test case
1303 """
juraj.linkesabec0122018-11-16 17:28:56 +01001304 if len(self.errors) > 0 or len(self.failures) > 0:
1305 self.stream.writeln()
1306 self.printErrorList('ERROR', self.errors)
1307 self.printErrorList('FAIL', self.failures)
1308
1309 # ^^ that is the last output from unittest before summary
1310 if not self.runner.print_summary:
1311 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1312 self.stream = devnull
1313 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001314
Damjan Marionf56b77a2016-10-03 19:44:57 +02001315 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001316 """
1317 Print error list to the output stream together with error type
1318 and test case description.
1319
1320 :param flavour: error type
1321 :param errors: iterable errors
1322
1323 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001324 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001325 self.stream.writeln(double_line_delim)
1326 self.stream.writeln("%s: %s" %
1327 (flavour, self.getDescription(test)))
1328 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001329 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001330
1331
Damjan Marionf56b77a2016-10-03 19:44:57 +02001332class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001333 """
Klement Sekera104543f2017-02-03 07:29:43 +01001334 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001335 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001336
Klement Sekeraf62ae122016-10-11 11:47:09 +02001337 @property
1338 def resultclass(self):
1339 """Class maintaining the results of the tests"""
1340 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001341
juraj.linkes184870a2018-07-16 14:22:01 +02001342 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001343 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001344 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001345 # ignore stream setting here, use hard-coded stdout to be in sync
1346 # with prints from VppTestCase methods ...
1347 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1348 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001349 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001350 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001351
juraj.linkesabec0122018-11-16 17:28:56 +01001352 self.orig_stream = self.stream
1353 self.resultclass.test_framework_result_pipe = result_pipe
1354
1355 self.print_summary = print_summary
1356
1357 def _makeResult(self):
1358 return self.resultclass(self.stream,
1359 self.descriptions,
1360 self.verbosity,
1361 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001362
Damjan Marionf56b77a2016-10-03 19:44:57 +02001363 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001364 """
1365 Run the tests
1366
1367 :param test:
1368
1369 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001370 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001371
1372 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001373 if not self.print_summary:
1374 self.stream = self.orig_stream
1375 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001376 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001377
1378
1379class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001380 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001381 self.logger = logger
1382 self.args = args
1383 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001384 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001385 super(Worker, self).__init__()
1386
1387 def run(self):
1388 executable = self.args[0]
1389 self.logger.debug("Running executable w/args `%s'" % self.args)
1390 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001391 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001392 env["CK_LOG_FILE_NAME"] = "-"
1393 self.process = subprocess.Popen(
1394 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1395 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1396 out, err = self.process.communicate()
1397 self.logger.debug("Finished running `%s'" % executable)
1398 self.logger.info("Return code is `%s'" % self.process.returncode)
1399 self.logger.info(single_line_delim)
1400 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1401 self.logger.info(single_line_delim)
1402 self.logger.info(out)
1403 self.logger.info(single_line_delim)
1404 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1405 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001406 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001407 self.logger.info(single_line_delim)
1408 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001409
1410if __name__ == '__main__':
1411 pass