blob: 989fb3d75cfaf36e4e29ff7e14022988fe9af925 [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
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400404 def wait_for_coredump(cls):
405 corefile = cls.tempdir + "/core"
406 if os.path.isfile(corefile):
407 cls.logger.error("Waiting for coredump to complete: %s", corefile)
408 curr_size = os.path.getsize(corefile)
409 deadline = time.time() + 60
410 ok = False
411 while time.time() < deadline:
412 cls.sleep(1)
413 size = curr_size
414 curr_size = os.path.getsize(corefile)
415 if size == curr_size:
416 ok = True
417 break
418 if not ok:
419 cls.logger.error("Timed out waiting for coredump to complete:"
420 " %s", corefile)
421 else:
422 cls.logger.error("Coredump complete: %s, size %d",
423 corefile, curr_size)
424
425 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200426 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200427 """
428 Perform class setup before running the testcase
429 Remove shared memory files, start vpp and connect the vpp-api
430 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800431 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100432 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100433 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100434 cls.logger = get_logger(cls.__name__)
435 if hasattr(cls, 'parallel_handler'):
436 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100437 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700438
Klement Sekeraf62ae122016-10-11 11:47:09 +0200439 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200440 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200441 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200442 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200443 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
444 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100445 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
446 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200447 cls.file_handler.setLevel(DEBUG)
448 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700449 cls.logger.debug("--- setUpClass() for %s called ---" %
450 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200451 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200452 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200453 cls.logger.info("Temporary dir is %s, shm prefix is %s",
454 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200455 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100456 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100457 cls._captures = []
458 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200459 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100460 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100461 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200462 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200463 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200464 # need to catch exceptions here because if we raise, then the cleanup
465 # doesn't get called and we might end with a zombie vpp
466 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200467 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200468 cls.reporter.send_keep_alive(cls, 'setUpClass')
469 VppTestResult.current_test_case_info = TestCaseInfo(
470 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100471 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100472 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100473 cls.pump_thread_stop_flag = Event()
474 cls.pump_thread_wakeup_pipe = os.pipe()
475 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100476 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100477 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200478 if cls.debug_gdb or cls.debug_gdbserver:
479 read_timeout = 0
480 else:
481 read_timeout = 5
482 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
483 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100484 if cls.step:
485 hook = StepHook(cls)
486 else:
487 hook = PollHook(cls)
488 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200489 cls.wait_for_stats_socket()
490 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200491 try:
492 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100493 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200494 cls.vpp_startup_failed = True
495 cls.logger.critical(
496 "VPP died shortly after startup, check the"
497 " output to standard error for possible cause")
498 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100499 try:
500 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100501 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100502 try:
503 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100504 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100505 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100506 if cls.debug_gdbserver:
507 print(colorize("You're running VPP inside gdbserver but "
508 "VPP-API connection failed, did you forget "
509 "to 'continue' VPP from within gdb?", RED))
510 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100511 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100512 try:
513 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100514 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100515 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100516 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200517
Damjan Marionf56b77a2016-10-03 19:44:57 +0200518 @classmethod
519 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200520 """
521 Disconnect vpp-api, kill vpp and cleanup shared memory files
522 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200523 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
524 cls.vpp.poll()
525 if cls.vpp.returncode is None:
526 print(double_line_delim)
527 print("VPP or GDB server is still running")
528 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800529 input("When done debugging, press ENTER to kill the "
530 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200531
juraj.linkes184870a2018-07-16 14:22:01 +0200532 # first signal that we want to stop the pump thread, then wake it up
533 if hasattr(cls, 'pump_thread_stop_flag'):
534 cls.pump_thread_stop_flag.set()
535 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100536 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100537 if hasattr(cls, 'pump_thread'):
538 cls.logger.debug("Waiting for pump thread to stop")
539 cls.pump_thread.join()
540 if hasattr(cls, 'vpp_stderr_reader_thread'):
541 cls.logger.debug("Waiting for stdderr pump to stop")
542 cls.vpp_stderr_reader_thread.join()
543
Klement Sekeraf62ae122016-10-11 11:47:09 +0200544 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100545 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700546 cls.logger.debug("Disconnecting class vapi client on %s",
547 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100548 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700549 cls.logger.debug("Deleting class vapi attribute on %s",
550 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100551 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200552 cls.vpp.poll()
553 if cls.vpp.returncode is None:
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400554 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100555 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400556 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100557 cls.logger.debug("Waiting for vpp to die")
558 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700559 cls.logger.debug("Deleting class vpp attribute on %s",
560 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200561 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200562
Klement Sekera3747c752017-04-10 06:30:17 +0200563 if cls.vpp_startup_failed:
564 stdout_log = cls.logger.info
565 stderr_log = cls.logger.critical
566 else:
567 stdout_log = cls.logger.info
568 stderr_log = cls.logger.info
569
Klement Sekerae4504c62016-12-08 10:16:41 +0100570 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200571 stdout_log(single_line_delim)
572 stdout_log('VPP output to stdout while running %s:', cls.__name__)
573 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100574 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200575 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
576 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200577 stdout_log('\n%s', vpp_output)
578 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200579
Klement Sekerae4504c62016-12-08 10:16:41 +0100580 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200581 stderr_log(single_line_delim)
582 stderr_log('VPP output to stderr while running %s:', cls.__name__)
583 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100584 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200585 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
586 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200587 stderr_log('\n%s', vpp_output)
588 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200589
Damjan Marionf56b77a2016-10-03 19:44:57 +0200590 @classmethod
591 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200592 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700593 cls.logger.debug("--- tearDownClass() for %s called ---" %
594 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200595 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200596 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200597 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100598 cls.reset_packet_infos()
599 if debug_framework:
600 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200601
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700602 def show_commands_at_teardown(self):
603 """ Allow subclass specific teardown logging additions."""
604 self.logger.info("--- No test specific show commands provided. ---")
605
Damjan Marionf56b77a2016-10-03 19:44:57 +0200606 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200607 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100608 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
609 (self.__class__.__name__, self._testMethodName,
610 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200611 if not self.vpp_dead:
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700612 self.logger.info(
613 "--- Logging show commands common to all testcases. ---")
Klement Sekerad91fa612019-01-15 13:25:09 +0100614 self.logger.debug(self.vapi.cli("show trace max 1000"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700615 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200616 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200617 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200618 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800619 self.logger.info(self.vapi.ppcli("show log"))
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700620 self.logger.info("Logging testcase specific show commands.")
621 self.show_commands_at_teardown()
Klement Sekera10db26f2017-01-11 08:16:53 +0100622 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500623 # Save/Dump VPP api trace log
624 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
625 tmp_api_trace = "/tmp/%s" % api_trace
626 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
627 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
628 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
629 vpp_api_trace_log))
630 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500631 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500632 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100633 else:
634 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200635
Damjan Marionf56b77a2016-10-03 19:44:57 +0200636 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200637 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800638 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200639 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100640 if self.vpp_dead:
641 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100642 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100643 self.vpp_stdout_deque.append(
644 "--- test setUp() for %s.%s(%s) starts here ---\n" %
645 (self.__class__.__name__, self._testMethodName,
646 self._testMethodDoc))
647 self.vpp_stderr_deque.append(
648 "--- test setUp() for %s.%s(%s) starts here ---\n" %
649 (self.__class__.__name__, self._testMethodName,
650 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200651 self.vapi.cli("clear trace")
652 # store the test instance inside the test class - so that objects
653 # holding the class can access instance methods (like assertEqual)
654 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200655
Damjan Marionf56b77a2016-10-03 19:44:57 +0200656 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200657 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200658 """
659 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200660
Klement Sekera75e7d132017-09-20 08:26:30 +0200661 :param interfaces: iterable interface indexes (if None,
662 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200663
Klement Sekeraf62ae122016-10-11 11:47:09 +0200664 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200665 if interfaces is None:
666 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200667 for i in interfaces:
668 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200669
Damjan Marionf56b77a2016-10-03 19:44:57 +0200670 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100671 def register_capture(cls, cap_name):
672 """ Register a capture in the testclass """
673 # add to the list of captures with current timestamp
674 cls._captures.append((time.time(), cap_name))
675 # filter out from zombies
676 cls._zombie_captures = [(stamp, name)
677 for (stamp, name) in cls._zombie_captures
678 if name != cap_name]
679
680 @classmethod
681 def pg_start(cls):
682 """ Remove any zombie captures and enable the packet generator """
683 # how long before capture is allowed to be deleted - otherwise vpp
684 # crashes - 100ms seems enough (this shouldn't be needed at all)
685 capture_ttl = 0.1
686 now = time.time()
687 for stamp, cap_name in cls._zombie_captures:
688 wait = stamp + capture_ttl - now
689 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100690 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100691 now = time.time()
692 cls.logger.debug("Removing zombie capture %s" % cap_name)
693 cls.vapi.cli('packet-generator delete %s' % cap_name)
694
Klement Sekerad91fa612019-01-15 13:25:09 +0100695 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200696 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100697 cls._zombie_captures = cls._captures
698 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200699
Damjan Marionf56b77a2016-10-03 19:44:57 +0200700 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200701 def create_pg_interfaces(cls, interfaces):
702 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100703 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200704
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100705 :param interfaces: iterable indexes of the interfaces.
706 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200707
Klement Sekeraf62ae122016-10-11 11:47:09 +0200708 """
709 result = []
710 for i in interfaces:
711 intf = VppPGInterface(cls, i)
712 setattr(cls, intf.name, intf)
713 result.append(intf)
714 cls.pg_interfaces = result
715 return result
716
Matej Klotton0178d522016-11-04 11:11:44 +0100717 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200718 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100719 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100720 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100721
Klement Sekerab9ef2732018-06-24 22:49:33 +0200722 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100723 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100724 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200725 result = [VppLoInterface(cls) for i in range(count)]
726 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100727 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100728 cls.lo_interfaces = result
729 return result
730
Neale Ranns192b13f2019-03-15 02:16:20 -0700731 @classmethod
732 def create_bvi_interfaces(cls, count):
733 """
734 Create BVI interfaces.
735
736 :param count: number of interfaces created.
737 :returns: List of created interfaces.
738 """
739 result = [VppBviInterface(cls) for i in range(count)]
740 for intf in result:
741 setattr(cls, intf.name, intf)
742 cls.bvi_interfaces = result
743 return result
744
Damjan Marionf56b77a2016-10-03 19:44:57 +0200745 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200746 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200747 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200748 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200749 NOTE: Currently works only when Raw layer is present.
750
751 :param packet: packet
752 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200753 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200754
755 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200756 packet_len = len(packet) + 4
757 extend = size - packet_len
758 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200759 num = (extend / len(padding)) + 1
760 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200761
Klement Sekeradab231a2016-12-21 08:50:14 +0100762 @classmethod
763 def reset_packet_infos(cls):
764 """ Reset the list of packet info objects and packet counts to zero """
765 cls._packet_infos = {}
766 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200767
Klement Sekeradab231a2016-12-21 08:50:14 +0100768 @classmethod
769 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200770 """
771 Create packet info object containing the source and destination indexes
772 and add it to the testcase's packet info list
773
Klement Sekeradab231a2016-12-21 08:50:14 +0100774 :param VppInterface src_if: source interface
775 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200776
777 :returns: _PacketInfo object
778
779 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200780 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100781 info.index = len(cls._packet_infos)
782 info.src = src_if.sw_if_index
783 info.dst = dst_if.sw_if_index
784 if isinstance(dst_if, VppSubInterface):
785 dst_idx = dst_if.parent.sw_if_index
786 else:
787 dst_idx = dst_if.sw_if_index
788 if dst_idx in cls._packet_count_for_dst_if_idx:
789 cls._packet_count_for_dst_if_idx[dst_idx] += 1
790 else:
791 cls._packet_count_for_dst_if_idx[dst_idx] = 1
792 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200793 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200794
Damjan Marionf56b77a2016-10-03 19:44:57 +0200795 @staticmethod
796 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200797 """
798 Convert _PacketInfo object to packet payload
799
800 :param info: _PacketInfo object
801
802 :returns: string containing serialized data from packet info
803 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100804 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
805 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200806
Damjan Marionf56b77a2016-10-03 19:44:57 +0200807 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800808 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200809 """
810 Convert packet payload to _PacketInfo object
811
812 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700813 :type payload: <class 'scapy.packet.Raw'>
814 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800815 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700816 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200817 :returns: _PacketInfo object containing de-serialized data from payload
818
819 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800820 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200821 info = _PacketInfo()
822 info.index = int(numbers[0])
823 info.src = int(numbers[1])
824 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100825 info.ip = int(numbers[3])
826 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200827 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200828
Damjan Marionf56b77a2016-10-03 19:44:57 +0200829 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200830 """
831 Iterate over the packet info list stored in the testcase
832 Start iteration with first element if info is None
833 Continue based on index in info if info is specified
834
835 :param info: info or None
836 :returns: next info in list or None if no more infos
837 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200838 if info is None:
839 next_index = 0
840 else:
841 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100842 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200843 return None
844 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100845 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200846
Klement Sekeraf62ae122016-10-11 11:47:09 +0200847 def get_next_packet_info_for_interface(self, src_index, info):
848 """
849 Search the packet info list for the next packet info with same source
850 interface index
851
852 :param src_index: source interface index to search for
853 :param info: packet info - where to start the search
854 :returns: packet info or None
855
856 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200857 while True:
858 info = self.get_next_packet_info(info)
859 if info is None:
860 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200861 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200862 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200863
Klement Sekeraf62ae122016-10-11 11:47:09 +0200864 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
865 """
866 Search the packet info list for the next packet info with same source
867 and destination interface indexes
868
869 :param src_index: source interface index to search for
870 :param dst_index: destination interface index to search for
871 :param info: packet info - where to start the search
872 :returns: packet info or None
873
874 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200875 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200876 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200877 if info is None:
878 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200879 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200880 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200881
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200882 def assert_equal(self, real_value, expected_value, name_or_class=None):
883 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100884 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200885 return
886 try:
887 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
888 msg = msg % (getdoc(name_or_class).strip(),
889 real_value, str(name_or_class(real_value)),
890 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100891 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200892 msg = "Invalid %s: %s does not match expected value %s" % (
893 name_or_class, real_value, expected_value)
894
895 self.assertEqual(real_value, expected_value, msg)
896
Klement Sekerab17dd962017-01-09 07:43:48 +0100897 def assert_in_range(self,
898 real_value,
899 expected_min,
900 expected_max,
901 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200902 if name is None:
903 msg = None
904 else:
905 msg = "Invalid %s: %s out of range <%s,%s>" % (
906 name, real_value, expected_min, expected_max)
907 self.assertTrue(expected_min <= real_value <= expected_max, msg)
908
Klement Sekerad81ae412018-05-16 10:52:54 +0200909 def assert_packet_checksums_valid(self, packet,
910 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700911 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200912 self.logger.debug(
913 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200914 udp_layers = ['UDP', 'UDPerror']
915 checksum_fields = ['cksum', 'chksum']
916 checksums = []
917 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700918 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200919 while True:
920 layer = temp.getlayer(counter)
921 if layer:
922 for cf in checksum_fields:
923 if hasattr(layer, cf):
924 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100925 0 == getattr(layer, cf) and \
926 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200927 continue
928 delattr(layer, cf)
929 checksums.append((counter, cf))
930 else:
931 break
932 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200933 if 0 == len(checksums):
934 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700935 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200936 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200937 calc_sum = getattr(temp[layer], cf)
938 self.assert_equal(
939 getattr(received[layer], cf), calc_sum,
940 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
941 self.logger.debug(
942 "Checksum field `%s` on `%s` layer has correct value `%s`" %
943 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200944
945 def assert_checksum_valid(self, received_packet, layer,
946 field_name='chksum',
947 ignore_zero_checksum=False):
948 """ Check checksum of received packet on given layer """
949 received_packet_checksum = getattr(received_packet[layer], field_name)
950 if ignore_zero_checksum and 0 == received_packet_checksum:
951 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700952 recalculated = received_packet.__class__(
953 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200954 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700955 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +0200956 self.assert_equal(received_packet_checksum,
957 getattr(recalculated[layer], field_name),
958 "packet checksum on layer: %s" % layer)
959
960 def assert_ip_checksum_valid(self, received_packet,
961 ignore_zero_checksum=False):
962 self.assert_checksum_valid(received_packet, 'IP',
963 ignore_zero_checksum=ignore_zero_checksum)
964
965 def assert_tcp_checksum_valid(self, received_packet,
966 ignore_zero_checksum=False):
967 self.assert_checksum_valid(received_packet, 'TCP',
968 ignore_zero_checksum=ignore_zero_checksum)
969
970 def assert_udp_checksum_valid(self, received_packet,
971 ignore_zero_checksum=True):
972 self.assert_checksum_valid(received_packet, 'UDP',
973 ignore_zero_checksum=ignore_zero_checksum)
974
975 def assert_embedded_icmp_checksum_valid(self, received_packet):
976 if received_packet.haslayer(IPerror):
977 self.assert_checksum_valid(received_packet, 'IPerror')
978 if received_packet.haslayer(TCPerror):
979 self.assert_checksum_valid(received_packet, 'TCPerror')
980 if received_packet.haslayer(UDPerror):
981 self.assert_checksum_valid(received_packet, 'UDPerror',
982 ignore_zero_checksum=True)
983 if received_packet.haslayer(ICMPerror):
984 self.assert_checksum_valid(received_packet, 'ICMPerror')
985
986 def assert_icmp_checksum_valid(self, received_packet):
987 self.assert_checksum_valid(received_packet, 'ICMP')
988 self.assert_embedded_icmp_checksum_valid(received_packet)
989
990 def assert_icmpv6_checksum_valid(self, pkt):
991 if pkt.haslayer(ICMPv6DestUnreach):
992 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
993 self.assert_embedded_icmp_checksum_valid(pkt)
994 if pkt.haslayer(ICMPv6EchoRequest):
995 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
996 if pkt.haslayer(ICMPv6EchoReply):
997 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
998
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100999 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +01001000 if counter.startswith("/"):
1001 counter_value = self.statistics.get_counter(counter)
1002 self.assert_equal(counter_value, expected_value,
1003 "packet counter `%s'" % counter)
1004 else:
1005 counters = self.vapi.cli("sh errors").split('\n')
1006 counter_value = -1
1007 for i in range(1, len(counters) - 1):
1008 results = counters[i].split()
1009 if results[1] == counter:
1010 counter_value = int(results[0])
1011 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001012
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001013 @classmethod
1014 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001015
1016 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1017 # * by Guido, only the main thread can be interrupted.
1018 # */
1019 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1020 if timeout == 0:
1021 # yield quantum
1022 if hasattr(os, 'sched_yield'):
1023 os.sched_yield()
1024 else:
1025 time.sleep(0)
1026 return
1027
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001028 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001029 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001030 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001031 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001032 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001033 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001034 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001035 "slept for %es instead of ~%es!",
1036 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001037 if hasattr(cls, 'logger'):
1038 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001039 "Finished sleep (%s) - slept %es (wanted %es)",
1040 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001041
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001042 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001043 self.vapi.cli("clear trace")
1044 intf.add_stream(pkts)
1045 self.pg_enable_capture(self.pg_interfaces)
1046 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001047
1048 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1049 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001050 if not timeout:
1051 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001052 for i in self.pg_interfaces:
1053 i.get_capture(0, timeout=timeout)
1054 i.assert_nothing_captured(remark=remark)
1055 timeout = 0.1
1056
Neale Rannsd7603d92019-03-28 08:56:10 +00001057 def send_and_expect(self, intf, pkts, output, n_rx=None):
1058 if not n_rx:
1059 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001060 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001061 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001062 return rx
1063
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001064 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1065 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001066 rx = output.get_capture(len(pkts))
1067 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001068 if not timeout:
1069 timeout = 1
1070 for i in self.pg_interfaces:
1071 if i not in outputs:
1072 i.get_capture(0, timeout=timeout)
1073 i.assert_nothing_captured()
1074 timeout = 0.1
1075
Neale Ranns52fae862018-01-08 04:41:42 -08001076 return rx
1077
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001078 def runTest(self):
1079 """ unittest calls runTest when TestCase is instantiated without a
1080 test case. Use case: Writing unittests against VppTestCase"""
1081 pass
1082
Damjan Marionf56b77a2016-10-03 19:44:57 +02001083
juraj.linkes184870a2018-07-16 14:22:01 +02001084def get_testcase_doc_name(test):
1085 return getdoc(test.__class__).splitlines()[0]
1086
1087
Ole Trøan5ba91592018-11-22 10:01:09 +00001088def get_test_description(descriptions, test):
1089 short_description = test.shortDescription()
1090 if descriptions and short_description:
1091 return short_description
1092 else:
1093 return str(test)
1094
1095
juraj.linkes40dd73b2018-09-21 13:55:16 +02001096class TestCaseInfo(object):
1097 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1098 self.logger = logger
1099 self.tempdir = tempdir
1100 self.vpp_pid = vpp_pid
1101 self.vpp_bin_path = vpp_bin_path
1102 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001103
1104
Damjan Marionf56b77a2016-10-03 19:44:57 +02001105class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001106 """
1107 @property result_string
1108 String variable to store the test case result string.
1109 @property errors
1110 List variable containing 2-tuples of TestCase instances and strings
1111 holding formatted tracebacks. Each tuple represents a test which
1112 raised an unexpected exception.
1113 @property failures
1114 List variable containing 2-tuples of TestCase instances and strings
1115 holding formatted tracebacks. Each tuple represents a test where
1116 a failure was explicitly signalled using the TestCase.assert*()
1117 methods.
1118 """
1119
juraj.linkes40dd73b2018-09-21 13:55:16 +02001120 failed_test_cases_info = set()
1121 core_crash_test_cases_info = set()
1122 current_test_case_info = None
1123
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001124 def __init__(self, stream=None, descriptions=None, verbosity=None,
1125 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001126 """
Klement Sekerada505f62017-01-04 12:58:53 +01001127 :param stream File descriptor to store where to report test results.
1128 Set to the standard error stream by default.
1129 :param descriptions Boolean variable to store information if to use
1130 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001131 :param verbosity Integer variable to store required verbosity level.
1132 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001133 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001134 self.stream = stream
1135 self.descriptions = descriptions
1136 self.verbosity = verbosity
1137 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001138 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001139
Damjan Marionf56b77a2016-10-03 19:44:57 +02001140 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001141 """
1142 Record a test succeeded result
1143
1144 :param test:
1145
1146 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001147 if self.current_test_case_info:
1148 self.current_test_case_info.logger.debug(
1149 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1150 test._testMethodName,
1151 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001152 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001153 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001154
juraj.linkescae64f82018-09-19 15:01:47 +02001155 self.send_result_through_pipe(test, PASS)
1156
Klement Sekeraf62ae122016-10-11 11:47:09 +02001157 def addSkip(self, test, reason):
1158 """
1159 Record a test skipped.
1160
1161 :param test:
1162 :param reason:
1163
1164 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001165 if self.current_test_case_info:
1166 self.current_test_case_info.logger.debug(
1167 "--- addSkip() %s.%s(%s) called, reason is %s" %
1168 (test.__class__.__name__, test._testMethodName,
1169 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001170 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001171 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001172
juraj.linkescae64f82018-09-19 15:01:47 +02001173 self.send_result_through_pipe(test, SKIP)
1174
juraj.linkes40dd73b2018-09-21 13:55:16 +02001175 def symlink_failed(self):
1176 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001177 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001178 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001179 link_path = os.path.join(
1180 failed_dir,
1181 '%s-FAILED' %
1182 os.path.basename(self.current_test_case_info.tempdir))
1183 if self.current_test_case_info.logger:
1184 self.current_test_case_info.logger.debug(
1185 "creating a link to the failed test")
1186 self.current_test_case_info.logger.debug(
1187 "os.symlink(%s, %s)" %
1188 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001189 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001190 if self.current_test_case_info.logger:
1191 self.current_test_case_info.logger.debug(
1192 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001193 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001194 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001195
Klement Sekeraf413bef2017-08-15 07:09:02 +02001196 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001197 if self.current_test_case_info.logger:
1198 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001199
juraj.linkescae64f82018-09-19 15:01:47 +02001200 def send_result_through_pipe(self, test, result):
1201 if hasattr(self, 'test_framework_result_pipe'):
1202 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001203 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001204 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001205
juraj.linkes40dd73b2018-09-21 13:55:16 +02001206 def log_error(self, test, err, fn_name):
1207 if self.current_test_case_info:
1208 if isinstance(test, unittest.suite._ErrorHolder):
1209 test_name = test.description
1210 else:
1211 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1212 test._testMethodName,
1213 test._testMethodDoc)
1214 self.current_test_case_info.logger.debug(
1215 "--- %s() %s called, err is %s" %
1216 (fn_name, test_name, err))
1217 self.current_test_case_info.logger.debug(
1218 "formatted exception is:\n%s" %
1219 "".join(format_exception(*err)))
1220
1221 def add_error(self, test, err, unittest_fn, error_type):
1222 if error_type == FAIL:
1223 self.log_error(test, err, 'addFailure')
1224 error_type_str = colorize("FAIL", RED)
1225 elif error_type == ERROR:
1226 self.log_error(test, err, 'addError')
1227 error_type_str = colorize("ERROR", RED)
1228 else:
1229 raise Exception('Error type %s cannot be used to record an '
1230 'error or a failure' % error_type)
1231
1232 unittest_fn(self, test, err)
1233 if self.current_test_case_info:
1234 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1235 (error_type_str,
1236 self.current_test_case_info.tempdir)
1237 self.symlink_failed()
1238 self.failed_test_cases_info.add(self.current_test_case_info)
1239 if is_core_present(self.current_test_case_info.tempdir):
1240 if not self.current_test_case_info.core_crash_test:
1241 if isinstance(test, unittest.suite._ErrorHolder):
1242 test_name = str(test)
1243 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001244 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001245 get_testcase_doc_name(test), test.id())
1246 self.current_test_case_info.core_crash_test = test_name
1247 self.core_crash_test_cases_info.add(
1248 self.current_test_case_info)
1249 else:
1250 self.result_string = '%s [no temp dir]' % error_type_str
1251
1252 self.send_result_through_pipe(test, error_type)
1253
Damjan Marionf56b77a2016-10-03 19:44:57 +02001254 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001255 """
1256 Record a test failed result
1257
1258 :param test:
1259 :param err: error message
1260
1261 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001262 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001263
Damjan Marionf56b77a2016-10-03 19:44:57 +02001264 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001265 """
1266 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001267
Klement Sekeraf62ae122016-10-11 11:47:09 +02001268 :param test:
1269 :param err: error message
1270
1271 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001272 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001273
Damjan Marionf56b77a2016-10-03 19:44:57 +02001274 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001275 """
1276 Get test description
1277
1278 :param test:
1279 :returns: test description
1280
1281 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001282 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001283
Damjan Marionf56b77a2016-10-03 19:44:57 +02001284 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001285 """
1286 Start a test
1287
1288 :param test:
1289
1290 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001291
1292 def print_header(test):
1293 if not hasattr(test.__class__, '_header_printed'):
1294 print(double_line_delim)
1295 print(colorize(getdoc(test).splitlines()[0], GREEN))
1296 print(double_line_delim)
1297 test.__class__._header_printed = True
1298
1299 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001300
Damjan Marionf56b77a2016-10-03 19:44:57 +02001301 unittest.TestResult.startTest(self, test)
1302 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001303 self.stream.writeln(
1304 "Starting " + self.getDescription(test) + " ...")
1305 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001306
Damjan Marionf56b77a2016-10-03 19:44:57 +02001307 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001308 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001309 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001310
1311 :param test:
1312
1313 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001314 unittest.TestResult.stopTest(self, test)
1315 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001316 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001317 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001318 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001319 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001320 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001321 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001322 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001323
1324 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001325
Damjan Marionf56b77a2016-10-03 19:44:57 +02001326 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001327 """
1328 Print errors from running the test case
1329 """
juraj.linkesabec0122018-11-16 17:28:56 +01001330 if len(self.errors) > 0 or len(self.failures) > 0:
1331 self.stream.writeln()
1332 self.printErrorList('ERROR', self.errors)
1333 self.printErrorList('FAIL', self.failures)
1334
1335 # ^^ that is the last output from unittest before summary
1336 if not self.runner.print_summary:
1337 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1338 self.stream = devnull
1339 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001340
Damjan Marionf56b77a2016-10-03 19:44:57 +02001341 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001342 """
1343 Print error list to the output stream together with error type
1344 and test case description.
1345
1346 :param flavour: error type
1347 :param errors: iterable errors
1348
1349 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001350 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001351 self.stream.writeln(double_line_delim)
1352 self.stream.writeln("%s: %s" %
1353 (flavour, self.getDescription(test)))
1354 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001355 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001356
1357
Damjan Marionf56b77a2016-10-03 19:44:57 +02001358class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001359 """
Klement Sekera104543f2017-02-03 07:29:43 +01001360 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001361 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001362
Klement Sekeraf62ae122016-10-11 11:47:09 +02001363 @property
1364 def resultclass(self):
1365 """Class maintaining the results of the tests"""
1366 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001367
juraj.linkes184870a2018-07-16 14:22:01 +02001368 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001369 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001370 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001371 # ignore stream setting here, use hard-coded stdout to be in sync
1372 # with prints from VppTestCase methods ...
1373 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1374 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001375 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001376 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001377
juraj.linkesabec0122018-11-16 17:28:56 +01001378 self.orig_stream = self.stream
1379 self.resultclass.test_framework_result_pipe = result_pipe
1380
1381 self.print_summary = print_summary
1382
1383 def _makeResult(self):
1384 return self.resultclass(self.stream,
1385 self.descriptions,
1386 self.verbosity,
1387 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001388
Damjan Marionf56b77a2016-10-03 19:44:57 +02001389 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001390 """
1391 Run the tests
1392
1393 :param test:
1394
1395 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001396 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001397
1398 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001399 if not self.print_summary:
1400 self.stream = self.orig_stream
1401 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001402 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001403
1404
1405class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001406 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001407 self.logger = logger
1408 self.args = args
1409 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001410 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001411 super(Worker, self).__init__()
1412
1413 def run(self):
1414 executable = self.args[0]
1415 self.logger.debug("Running executable w/args `%s'" % self.args)
1416 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001417 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001418 env["CK_LOG_FILE_NAME"] = "-"
1419 self.process = subprocess.Popen(
1420 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1421 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1422 out, err = self.process.communicate()
1423 self.logger.debug("Finished running `%s'" % executable)
1424 self.logger.info("Return code is `%s'" % self.process.returncode)
1425 self.logger.info(single_line_delim)
1426 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1427 self.logger.info(single_line_delim)
1428 self.logger.info(out)
1429 self.logger.info(single_line_delim)
1430 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1431 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001432 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001433 self.logger.info(single_line_delim)
1434 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001435
1436if __name__ == '__main__':
1437 pass