blob: 85bd6616c8bd843b3e876b928c1a1a694699f20f [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')
Dave Barach7d31ab22019-05-08 19:18:18 -0400289 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100290 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
291 plugin_path = None
292 if cls.plugin_path is not None:
293 if cls.extern_plugin_path is not None:
294 plugin_path = "%s:%s" % (
295 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100296 else:
297 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100298 elif cls.extern_plugin_path is not None:
299 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100300 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100301 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100302 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100303 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100304 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100305 if size is not None:
306 coredump_size = "coredump-size %s" % size
307 if coredump_size is None:
308 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200309
Ole Troana45dc072018-12-21 16:04:22 +0100310 cpu_core_number = cls.get_least_used_cpu()
juraj.linkes184870a2018-07-16 14:22:01 +0200311
Ole Troana45dc072018-12-21 16:04:22 +0100312 cls.vpp_cmdline = [cls.vpp_bin, "unix",
313 "{", "nodaemon", debug_cli, "full-coredump",
314 coredump_size, "runtime-dir", cls.tempdir, "}",
315 "api-trace", "{", "on", "}", "api-segment", "{",
316 "prefix", cls.shm_prefix, "}", "cpu", "{",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200317 "main-core", str(cpu_core_number), "}",
318 "statseg", "{", "socket-name", cls.stats_sock, "}",
319 "socksvr", "{", "socket-name", cls.api_sock, "}",
320 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100321 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200322 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100323 "}", "plugin", "unittest_plugin.so", "{", "enable",
324 "}"] + cls.extra_vpp_plugin_config + ["}", ]
325 if cls.extra_vpp_punt_config is not None:
326 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100327 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100328 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400329 if cls.test_plugin_path is not None:
330 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
331
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100332 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
333 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200334
335 @classmethod
336 def wait_for_enter(cls):
337 if cls.debug_gdbserver:
338 print(double_line_delim)
339 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
340 elif cls.debug_gdb:
341 print(double_line_delim)
342 print("Spawned VPP with PID: %d" % cls.vpp.pid)
343 else:
344 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
345 return
346 print(single_line_delim)
347 print("You can debug the VPP using e.g.:")
348 if cls.debug_gdbserver:
349 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
350 print("Now is the time to attach a gdb by running the above "
351 "command, set up breakpoints etc. and then resume VPP from "
352 "within gdb by issuing the 'continue' command")
353 elif cls.debug_gdb:
354 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
355 print("Now is the time to attach a gdb by running the above "
356 "command and set up breakpoints etc.")
357 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800358 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200359
360 @classmethod
361 def run_vpp(cls):
362 cmdline = cls.vpp_cmdline
363
364 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100365 gdbserver = '/usr/bin/gdbserver'
366 if not os.path.isfile(gdbserver) or \
367 not os.access(gdbserver, os.X_OK):
368 raise Exception("gdbserver binary '%s' does not exist or is "
369 "not executable" % gdbserver)
370
371 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200372 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
373
Klement Sekera931be3a2016-11-03 05:36:01 +0100374 try:
375 cls.vpp = subprocess.Popen(cmdline,
376 stdout=subprocess.PIPE,
377 stderr=subprocess.PIPE,
378 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800379 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800380 cls.logger.critical("Subprocess returned with non-0 return code: ("
381 "%s)", e.returncode)
382 raise
383 except OSError as e:
384 cls.logger.critical("Subprocess returned with OS error: "
385 "(%s) %s", e.errno, e.strerror)
386 raise
387 except Exception as e:
388 cls.logger.exception("Subprocess returned unexpected from "
389 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100390 raise
391
Klement Sekera277b89c2016-10-28 13:20:27 +0200392 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100393
Damjan Marionf56b77a2016-10-03 19:44:57 +0200394 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200395 def wait_for_stats_socket(cls):
396 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800397 ok = False
398 while time.time() < deadline or \
399 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200400 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800401 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200402 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700403 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800404 if not ok:
405 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200406
407 @classmethod
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400408 def wait_for_coredump(cls):
409 corefile = cls.tempdir + "/core"
410 if os.path.isfile(corefile):
411 cls.logger.error("Waiting for coredump to complete: %s", corefile)
412 curr_size = os.path.getsize(corefile)
413 deadline = time.time() + 60
414 ok = False
415 while time.time() < deadline:
416 cls.sleep(1)
417 size = curr_size
418 curr_size = os.path.getsize(corefile)
419 if size == curr_size:
420 ok = True
421 break
422 if not ok:
423 cls.logger.error("Timed out waiting for coredump to complete:"
424 " %s", corefile)
425 else:
426 cls.logger.error("Coredump complete: %s, size %d",
427 corefile, curr_size)
428
429 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200430 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200431 """
432 Perform class setup before running the testcase
433 Remove shared memory files, start vpp and connect the vpp-api
434 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800435 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100436 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100437 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100438 cls.logger = get_logger(cls.__name__)
439 if hasattr(cls, 'parallel_handler'):
440 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100441 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700442
Klement Sekeraf62ae122016-10-11 11:47:09 +0200443 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200444 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200445 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200446 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200447 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
448 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100449 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
450 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200451 cls.file_handler.setLevel(DEBUG)
452 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700453 cls.logger.debug("--- setUpClass() for %s called ---" %
454 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200455 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200456 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200457 cls.logger.info("Temporary dir is %s, shm prefix is %s",
458 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200459 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100460 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100461 cls._captures = []
462 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200463 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100464 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100465 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200466 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200467 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200468 # need to catch exceptions here because if we raise, then the cleanup
469 # doesn't get called and we might end with a zombie vpp
470 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200471 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200472 cls.reporter.send_keep_alive(cls, 'setUpClass')
473 VppTestResult.current_test_case_info = TestCaseInfo(
474 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100475 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100476 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100477 cls.pump_thread_stop_flag = Event()
478 cls.pump_thread_wakeup_pipe = os.pipe()
479 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100480 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100481 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200482 if cls.debug_gdb or cls.debug_gdbserver:
483 read_timeout = 0
484 else:
485 read_timeout = 5
486 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
487 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100488 if cls.step:
489 hook = StepHook(cls)
490 else:
491 hook = PollHook(cls)
492 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200493 cls.wait_for_stats_socket()
494 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200495 try:
496 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100497 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200498 cls.vpp_startup_failed = True
499 cls.logger.critical(
500 "VPP died shortly after startup, check the"
501 " output to standard error for possible cause")
502 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100503 try:
504 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100505 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100506 try:
507 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100508 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100509 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100510 if cls.debug_gdbserver:
511 print(colorize("You're running VPP inside gdbserver but "
512 "VPP-API connection failed, did you forget "
513 "to 'continue' VPP from within gdb?", RED))
514 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100515 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100516 try:
517 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100518 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100519 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100520 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200521
Damjan Marionf56b77a2016-10-03 19:44:57 +0200522 @classmethod
523 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200524 """
525 Disconnect vpp-api, kill vpp and cleanup shared memory files
526 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200527 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
528 cls.vpp.poll()
529 if cls.vpp.returncode is None:
530 print(double_line_delim)
531 print("VPP or GDB server is still running")
532 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800533 input("When done debugging, press ENTER to kill the "
534 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200535
juraj.linkes184870a2018-07-16 14:22:01 +0200536 # first signal that we want to stop the pump thread, then wake it up
537 if hasattr(cls, 'pump_thread_stop_flag'):
538 cls.pump_thread_stop_flag.set()
539 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100540 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100541 if hasattr(cls, 'pump_thread'):
542 cls.logger.debug("Waiting for pump thread to stop")
543 cls.pump_thread.join()
544 if hasattr(cls, 'vpp_stderr_reader_thread'):
545 cls.logger.debug("Waiting for stdderr pump to stop")
546 cls.vpp_stderr_reader_thread.join()
547
Klement Sekeraf62ae122016-10-11 11:47:09 +0200548 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100549 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700550 cls.logger.debug("Disconnecting class vapi client on %s",
551 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100552 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700553 cls.logger.debug("Deleting class vapi attribute on %s",
554 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100555 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200556 cls.vpp.poll()
557 if cls.vpp.returncode is None:
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400558 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100559 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400560 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100561 cls.logger.debug("Waiting for vpp to die")
562 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700563 cls.logger.debug("Deleting class vpp attribute on %s",
564 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200565 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200566
Klement Sekera3747c752017-04-10 06:30:17 +0200567 if cls.vpp_startup_failed:
568 stdout_log = cls.logger.info
569 stderr_log = cls.logger.critical
570 else:
571 stdout_log = cls.logger.info
572 stderr_log = cls.logger.info
573
Klement Sekerae4504c62016-12-08 10:16:41 +0100574 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200575 stdout_log(single_line_delim)
576 stdout_log('VPP output to stdout while running %s:', cls.__name__)
577 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100578 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200579 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
580 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200581 stdout_log('\n%s', vpp_output)
582 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200583
Klement Sekerae4504c62016-12-08 10:16:41 +0100584 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200585 stderr_log(single_line_delim)
586 stderr_log('VPP output to stderr while running %s:', cls.__name__)
587 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100588 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200589 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
590 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200591 stderr_log('\n%s', vpp_output)
592 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200593
Damjan Marionf56b77a2016-10-03 19:44:57 +0200594 @classmethod
595 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200596 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700597 cls.logger.debug("--- tearDownClass() for %s called ---" %
598 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200599 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200600 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200601 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100602 cls.reset_packet_infos()
603 if debug_framework:
604 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200605
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700606 def show_commands_at_teardown(self):
607 """ Allow subclass specific teardown logging additions."""
608 self.logger.info("--- No test specific show commands provided. ---")
609
Damjan Marionf56b77a2016-10-03 19:44:57 +0200610 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200611 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100612 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
613 (self.__class__.__name__, self._testMethodName,
614 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200615 if not self.vpp_dead:
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700616 self.logger.info(
617 "--- Logging show commands common to all testcases. ---")
Klement Sekerad91fa612019-01-15 13:25:09 +0100618 self.logger.debug(self.vapi.cli("show trace max 1000"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700619 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200620 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200621 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200622 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800623 self.logger.info(self.vapi.ppcli("show log"))
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700624 self.logger.info("Logging testcase specific show commands.")
625 self.show_commands_at_teardown()
Klement Sekera10db26f2017-01-11 08:16:53 +0100626 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500627 # Save/Dump VPP api trace log
628 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
629 tmp_api_trace = "/tmp/%s" % api_trace
630 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
631 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
632 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
633 vpp_api_trace_log))
634 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500635 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500636 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100637 else:
638 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200639
Damjan Marionf56b77a2016-10-03 19:44:57 +0200640 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200641 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800642 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200643 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100644 if self.vpp_dead:
645 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100646 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100647 self.vpp_stdout_deque.append(
648 "--- test setUp() for %s.%s(%s) starts here ---\n" %
649 (self.__class__.__name__, self._testMethodName,
650 self._testMethodDoc))
651 self.vpp_stderr_deque.append(
652 "--- test setUp() for %s.%s(%s) starts here ---\n" %
653 (self.__class__.__name__, self._testMethodName,
654 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200655 self.vapi.cli("clear trace")
656 # store the test instance inside the test class - so that objects
657 # holding the class can access instance methods (like assertEqual)
658 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200659
Damjan Marionf56b77a2016-10-03 19:44:57 +0200660 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200661 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200662 """
663 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200664
Klement Sekera75e7d132017-09-20 08:26:30 +0200665 :param interfaces: iterable interface indexes (if None,
666 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200667
Klement Sekeraf62ae122016-10-11 11:47:09 +0200668 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200669 if interfaces is None:
670 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200671 for i in interfaces:
672 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200673
Damjan Marionf56b77a2016-10-03 19:44:57 +0200674 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100675 def register_capture(cls, cap_name):
676 """ Register a capture in the testclass """
677 # add to the list of captures with current timestamp
678 cls._captures.append((time.time(), cap_name))
679 # filter out from zombies
680 cls._zombie_captures = [(stamp, name)
681 for (stamp, name) in cls._zombie_captures
682 if name != cap_name]
683
684 @classmethod
685 def pg_start(cls):
686 """ Remove any zombie captures and enable the packet generator """
687 # how long before capture is allowed to be deleted - otherwise vpp
688 # crashes - 100ms seems enough (this shouldn't be needed at all)
689 capture_ttl = 0.1
690 now = time.time()
691 for stamp, cap_name in cls._zombie_captures:
692 wait = stamp + capture_ttl - now
693 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100694 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100695 now = time.time()
696 cls.logger.debug("Removing zombie capture %s" % cap_name)
697 cls.vapi.cli('packet-generator delete %s' % cap_name)
698
Klement Sekerad91fa612019-01-15 13:25:09 +0100699 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200700 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100701 cls._zombie_captures = cls._captures
702 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200703
Damjan Marionf56b77a2016-10-03 19:44:57 +0200704 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200705 def create_pg_interfaces(cls, interfaces):
706 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100707 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200708
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100709 :param interfaces: iterable indexes of the interfaces.
710 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200711
Klement Sekeraf62ae122016-10-11 11:47:09 +0200712 """
713 result = []
714 for i in interfaces:
715 intf = VppPGInterface(cls, i)
716 setattr(cls, intf.name, intf)
717 result.append(intf)
718 cls.pg_interfaces = result
719 return result
720
Matej Klotton0178d522016-11-04 11:11:44 +0100721 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200722 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100723 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100724 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100725
Klement Sekerab9ef2732018-06-24 22:49:33 +0200726 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100727 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100728 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200729 result = [VppLoInterface(cls) for i in range(count)]
730 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100731 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100732 cls.lo_interfaces = result
733 return result
734
Neale Ranns192b13f2019-03-15 02:16:20 -0700735 @classmethod
736 def create_bvi_interfaces(cls, count):
737 """
738 Create BVI interfaces.
739
740 :param count: number of interfaces created.
741 :returns: List of created interfaces.
742 """
743 result = [VppBviInterface(cls) for i in range(count)]
744 for intf in result:
745 setattr(cls, intf.name, intf)
746 cls.bvi_interfaces = result
747 return result
748
Damjan Marionf56b77a2016-10-03 19:44:57 +0200749 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200750 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200751 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200752 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200753 NOTE: Currently works only when Raw layer is present.
754
755 :param packet: packet
756 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200757 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200758
759 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200760 packet_len = len(packet) + 4
761 extend = size - packet_len
762 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200763 num = (extend // len(padding)) + 1
764 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200765
Klement Sekeradab231a2016-12-21 08:50:14 +0100766 @classmethod
767 def reset_packet_infos(cls):
768 """ Reset the list of packet info objects and packet counts to zero """
769 cls._packet_infos = {}
770 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200771
Klement Sekeradab231a2016-12-21 08:50:14 +0100772 @classmethod
773 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200774 """
775 Create packet info object containing the source and destination indexes
776 and add it to the testcase's packet info list
777
Klement Sekeradab231a2016-12-21 08:50:14 +0100778 :param VppInterface src_if: source interface
779 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200780
781 :returns: _PacketInfo object
782
783 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100785 info.index = len(cls._packet_infos)
786 info.src = src_if.sw_if_index
787 info.dst = dst_if.sw_if_index
788 if isinstance(dst_if, VppSubInterface):
789 dst_idx = dst_if.parent.sw_if_index
790 else:
791 dst_idx = dst_if.sw_if_index
792 if dst_idx in cls._packet_count_for_dst_if_idx:
793 cls._packet_count_for_dst_if_idx[dst_idx] += 1
794 else:
795 cls._packet_count_for_dst_if_idx[dst_idx] = 1
796 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200797 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200798
Damjan Marionf56b77a2016-10-03 19:44:57 +0200799 @staticmethod
800 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200801 """
802 Convert _PacketInfo object to packet payload
803
804 :param info: _PacketInfo object
805
806 :returns: string containing serialized data from packet info
807 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100808 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
809 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200810
Damjan Marionf56b77a2016-10-03 19:44:57 +0200811 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800812 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200813 """
814 Convert packet payload to _PacketInfo object
815
816 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700817 :type payload: <class 'scapy.packet.Raw'>
818 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800819 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700820 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200821 :returns: _PacketInfo object containing de-serialized data from payload
822
823 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800824 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200825 info = _PacketInfo()
826 info.index = int(numbers[0])
827 info.src = int(numbers[1])
828 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100829 info.ip = int(numbers[3])
830 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200831 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200832
Damjan Marionf56b77a2016-10-03 19:44:57 +0200833 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200834 """
835 Iterate over the packet info list stored in the testcase
836 Start iteration with first element if info is None
837 Continue based on index in info if info is specified
838
839 :param info: info or None
840 :returns: next info in list or None if no more infos
841 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200842 if info is None:
843 next_index = 0
844 else:
845 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100846 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200847 return None
848 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100849 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200850
Klement Sekeraf62ae122016-10-11 11:47:09 +0200851 def get_next_packet_info_for_interface(self, src_index, info):
852 """
853 Search the packet info list for the next packet info with same source
854 interface index
855
856 :param src_index: source interface index to search for
857 :param info: packet info - where to start the search
858 :returns: packet info or None
859
860 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200861 while True:
862 info = self.get_next_packet_info(info)
863 if info is None:
864 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200865 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200866 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200867
Klement Sekeraf62ae122016-10-11 11:47:09 +0200868 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
869 """
870 Search the packet info list for the next packet info with same source
871 and destination interface indexes
872
873 :param src_index: source interface index to search for
874 :param dst_index: destination interface index to search for
875 :param info: packet info - where to start the search
876 :returns: packet info or None
877
878 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200879 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200880 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200881 if info is None:
882 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200883 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200884 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200885
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200886 def assert_equal(self, real_value, expected_value, name_or_class=None):
887 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100888 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200889 return
890 try:
891 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
892 msg = msg % (getdoc(name_or_class).strip(),
893 real_value, str(name_or_class(real_value)),
894 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100895 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200896 msg = "Invalid %s: %s does not match expected value %s" % (
897 name_or_class, real_value, expected_value)
898
899 self.assertEqual(real_value, expected_value, msg)
900
Klement Sekerab17dd962017-01-09 07:43:48 +0100901 def assert_in_range(self,
902 real_value,
903 expected_min,
904 expected_max,
905 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200906 if name is None:
907 msg = None
908 else:
909 msg = "Invalid %s: %s out of range <%s,%s>" % (
910 name, real_value, expected_min, expected_max)
911 self.assertTrue(expected_min <= real_value <= expected_max, msg)
912
Klement Sekerad81ae412018-05-16 10:52:54 +0200913 def assert_packet_checksums_valid(self, packet,
914 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700915 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200916 self.logger.debug(
917 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200918 udp_layers = ['UDP', 'UDPerror']
919 checksum_fields = ['cksum', 'chksum']
920 checksums = []
921 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700922 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200923 while True:
924 layer = temp.getlayer(counter)
925 if layer:
926 for cf in checksum_fields:
927 if hasattr(layer, cf):
928 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100929 0 == getattr(layer, cf) and \
930 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200931 continue
932 delattr(layer, cf)
933 checksums.append((counter, cf))
934 else:
935 break
936 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200937 if 0 == len(checksums):
938 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700939 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200940 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200941 calc_sum = getattr(temp[layer], cf)
942 self.assert_equal(
943 getattr(received[layer], cf), calc_sum,
944 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
945 self.logger.debug(
946 "Checksum field `%s` on `%s` layer has correct value `%s`" %
947 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200948
949 def assert_checksum_valid(self, received_packet, layer,
950 field_name='chksum',
951 ignore_zero_checksum=False):
952 """ Check checksum of received packet on given layer """
953 received_packet_checksum = getattr(received_packet[layer], field_name)
954 if ignore_zero_checksum and 0 == received_packet_checksum:
955 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700956 recalculated = received_packet.__class__(
957 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200958 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700959 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +0200960 self.assert_equal(received_packet_checksum,
961 getattr(recalculated[layer], field_name),
962 "packet checksum on layer: %s" % layer)
963
964 def assert_ip_checksum_valid(self, received_packet,
965 ignore_zero_checksum=False):
966 self.assert_checksum_valid(received_packet, 'IP',
967 ignore_zero_checksum=ignore_zero_checksum)
968
969 def assert_tcp_checksum_valid(self, received_packet,
970 ignore_zero_checksum=False):
971 self.assert_checksum_valid(received_packet, 'TCP',
972 ignore_zero_checksum=ignore_zero_checksum)
973
974 def assert_udp_checksum_valid(self, received_packet,
975 ignore_zero_checksum=True):
976 self.assert_checksum_valid(received_packet, 'UDP',
977 ignore_zero_checksum=ignore_zero_checksum)
978
979 def assert_embedded_icmp_checksum_valid(self, received_packet):
980 if received_packet.haslayer(IPerror):
981 self.assert_checksum_valid(received_packet, 'IPerror')
982 if received_packet.haslayer(TCPerror):
983 self.assert_checksum_valid(received_packet, 'TCPerror')
984 if received_packet.haslayer(UDPerror):
985 self.assert_checksum_valid(received_packet, 'UDPerror',
986 ignore_zero_checksum=True)
987 if received_packet.haslayer(ICMPerror):
988 self.assert_checksum_valid(received_packet, 'ICMPerror')
989
990 def assert_icmp_checksum_valid(self, received_packet):
991 self.assert_checksum_valid(received_packet, 'ICMP')
992 self.assert_embedded_icmp_checksum_valid(received_packet)
993
994 def assert_icmpv6_checksum_valid(self, pkt):
995 if pkt.haslayer(ICMPv6DestUnreach):
996 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
997 self.assert_embedded_icmp_checksum_valid(pkt)
998 if pkt.haslayer(ICMPv6EchoRequest):
999 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1000 if pkt.haslayer(ICMPv6EchoReply):
1001 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1002
Klement Sekera3a343d42019-05-16 14:35:46 +02001003 def get_packet_counter(self, counter):
1004 if counter.startswith("/"):
1005 counter_value = self.statistics.get_counter(counter)
1006 else:
1007 counters = self.vapi.cli("sh errors").split('\n')
1008 counter_value = -1
1009 for i in range(1, len(counters) - 1):
1010 results = counters[i].split()
1011 if results[1] == counter:
1012 counter_value = int(results[0])
1013 break
1014 return counter_value
1015
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001016 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +01001017 if counter.startswith("/"):
1018 counter_value = self.statistics.get_counter(counter)
1019 self.assert_equal(counter_value, expected_value,
1020 "packet counter `%s'" % counter)
1021 else:
1022 counters = self.vapi.cli("sh errors").split('\n')
1023 counter_value = -1
1024 for i in range(1, len(counters) - 1):
1025 results = counters[i].split()
1026 if results[1] == counter:
1027 counter_value = int(results[0])
1028 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001029
Ole Troan233e4682019-05-16 15:01:34 +02001030 def assert_error_counter_equal(self, counter, expected_value):
1031 counter_value = self.statistics.get_err_counter(counter)
1032 self.assert_equal(counter_value, expected_value,
1033 "error counter `%s'" % counter)
1034
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001035 @classmethod
1036 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001037
1038 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1039 # * by Guido, only the main thread can be interrupted.
1040 # */
1041 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1042 if timeout == 0:
1043 # yield quantum
1044 if hasattr(os, 'sched_yield'):
1045 os.sched_yield()
1046 else:
1047 time.sleep(0)
1048 return
1049
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001050 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001051 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001052 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001053 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001054 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001055 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001056 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001057 "slept for %es instead of ~%es!",
1058 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001059 if hasattr(cls, 'logger'):
1060 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001061 "Finished sleep (%s) - slept %es (wanted %es)",
1062 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001063
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001064 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001065 self.vapi.cli("clear trace")
1066 intf.add_stream(pkts)
1067 self.pg_enable_capture(self.pg_interfaces)
1068 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001069
1070 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1071 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001072 if not timeout:
1073 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001074 for i in self.pg_interfaces:
1075 i.get_capture(0, timeout=timeout)
1076 i.assert_nothing_captured(remark=remark)
1077 timeout = 0.1
1078
Neale Rannsd7603d92019-03-28 08:56:10 +00001079 def send_and_expect(self, intf, pkts, output, n_rx=None):
1080 if not n_rx:
1081 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001082 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001083 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001084 return rx
1085
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001086 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1087 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001088 rx = output.get_capture(len(pkts))
1089 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001090 if not timeout:
1091 timeout = 1
1092 for i in self.pg_interfaces:
1093 if i not in outputs:
1094 i.get_capture(0, timeout=timeout)
1095 i.assert_nothing_captured()
1096 timeout = 0.1
1097
Neale Ranns52fae862018-01-08 04:41:42 -08001098 return rx
1099
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001100 def runTest(self):
1101 """ unittest calls runTest when TestCase is instantiated without a
1102 test case. Use case: Writing unittests against VppTestCase"""
1103 pass
1104
Damjan Marionf56b77a2016-10-03 19:44:57 +02001105
juraj.linkes184870a2018-07-16 14:22:01 +02001106def get_testcase_doc_name(test):
1107 return getdoc(test.__class__).splitlines()[0]
1108
1109
Ole Trøan5ba91592018-11-22 10:01:09 +00001110def get_test_description(descriptions, test):
1111 short_description = test.shortDescription()
1112 if descriptions and short_description:
1113 return short_description
1114 else:
1115 return str(test)
1116
1117
juraj.linkes40dd73b2018-09-21 13:55:16 +02001118class TestCaseInfo(object):
1119 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1120 self.logger = logger
1121 self.tempdir = tempdir
1122 self.vpp_pid = vpp_pid
1123 self.vpp_bin_path = vpp_bin_path
1124 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001125
1126
Damjan Marionf56b77a2016-10-03 19:44:57 +02001127class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001128 """
1129 @property result_string
1130 String variable to store the test case result string.
1131 @property errors
1132 List variable containing 2-tuples of TestCase instances and strings
1133 holding formatted tracebacks. Each tuple represents a test which
1134 raised an unexpected exception.
1135 @property failures
1136 List variable containing 2-tuples of TestCase instances and strings
1137 holding formatted tracebacks. Each tuple represents a test where
1138 a failure was explicitly signalled using the TestCase.assert*()
1139 methods.
1140 """
1141
juraj.linkes40dd73b2018-09-21 13:55:16 +02001142 failed_test_cases_info = set()
1143 core_crash_test_cases_info = set()
1144 current_test_case_info = None
1145
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001146 def __init__(self, stream=None, descriptions=None, verbosity=None,
1147 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001148 """
Klement Sekerada505f62017-01-04 12:58:53 +01001149 :param stream File descriptor to store where to report test results.
1150 Set to the standard error stream by default.
1151 :param descriptions Boolean variable to store information if to use
1152 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001153 :param verbosity Integer variable to store required verbosity level.
1154 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001155 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001156 self.stream = stream
1157 self.descriptions = descriptions
1158 self.verbosity = verbosity
1159 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001160 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001161
Damjan Marionf56b77a2016-10-03 19:44:57 +02001162 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001163 """
1164 Record a test succeeded result
1165
1166 :param test:
1167
1168 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001169 if self.current_test_case_info:
1170 self.current_test_case_info.logger.debug(
1171 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1172 test._testMethodName,
1173 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001174 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001175 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001176
juraj.linkescae64f82018-09-19 15:01:47 +02001177 self.send_result_through_pipe(test, PASS)
1178
Klement Sekeraf62ae122016-10-11 11:47:09 +02001179 def addSkip(self, test, reason):
1180 """
1181 Record a test skipped.
1182
1183 :param test:
1184 :param reason:
1185
1186 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001187 if self.current_test_case_info:
1188 self.current_test_case_info.logger.debug(
1189 "--- addSkip() %s.%s(%s) called, reason is %s" %
1190 (test.__class__.__name__, test._testMethodName,
1191 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001192 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001193 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001194
juraj.linkescae64f82018-09-19 15:01:47 +02001195 self.send_result_through_pipe(test, SKIP)
1196
juraj.linkes40dd73b2018-09-21 13:55:16 +02001197 def symlink_failed(self):
1198 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001199 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001200 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001201 link_path = os.path.join(
1202 failed_dir,
1203 '%s-FAILED' %
1204 os.path.basename(self.current_test_case_info.tempdir))
1205 if self.current_test_case_info.logger:
1206 self.current_test_case_info.logger.debug(
1207 "creating a link to the failed test")
1208 self.current_test_case_info.logger.debug(
1209 "os.symlink(%s, %s)" %
1210 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001211 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001212 if self.current_test_case_info.logger:
1213 self.current_test_case_info.logger.debug(
1214 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001215 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001216 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001217
Klement Sekeraf413bef2017-08-15 07:09:02 +02001218 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001219 if self.current_test_case_info.logger:
1220 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001221
juraj.linkescae64f82018-09-19 15:01:47 +02001222 def send_result_through_pipe(self, test, result):
1223 if hasattr(self, 'test_framework_result_pipe'):
1224 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001225 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001226 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001227
juraj.linkes40dd73b2018-09-21 13:55:16 +02001228 def log_error(self, test, err, fn_name):
1229 if self.current_test_case_info:
1230 if isinstance(test, unittest.suite._ErrorHolder):
1231 test_name = test.description
1232 else:
1233 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1234 test._testMethodName,
1235 test._testMethodDoc)
1236 self.current_test_case_info.logger.debug(
1237 "--- %s() %s called, err is %s" %
1238 (fn_name, test_name, err))
1239 self.current_test_case_info.logger.debug(
1240 "formatted exception is:\n%s" %
1241 "".join(format_exception(*err)))
1242
1243 def add_error(self, test, err, unittest_fn, error_type):
1244 if error_type == FAIL:
1245 self.log_error(test, err, 'addFailure')
1246 error_type_str = colorize("FAIL", RED)
1247 elif error_type == ERROR:
1248 self.log_error(test, err, 'addError')
1249 error_type_str = colorize("ERROR", RED)
1250 else:
1251 raise Exception('Error type %s cannot be used to record an '
1252 'error or a failure' % error_type)
1253
1254 unittest_fn(self, test, err)
1255 if self.current_test_case_info:
1256 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1257 (error_type_str,
1258 self.current_test_case_info.tempdir)
1259 self.symlink_failed()
1260 self.failed_test_cases_info.add(self.current_test_case_info)
1261 if is_core_present(self.current_test_case_info.tempdir):
1262 if not self.current_test_case_info.core_crash_test:
1263 if isinstance(test, unittest.suite._ErrorHolder):
1264 test_name = str(test)
1265 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001266 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001267 get_testcase_doc_name(test), test.id())
1268 self.current_test_case_info.core_crash_test = test_name
1269 self.core_crash_test_cases_info.add(
1270 self.current_test_case_info)
1271 else:
1272 self.result_string = '%s [no temp dir]' % error_type_str
1273
1274 self.send_result_through_pipe(test, error_type)
1275
Damjan Marionf56b77a2016-10-03 19:44:57 +02001276 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001277 """
1278 Record a test failed result
1279
1280 :param test:
1281 :param err: error message
1282
1283 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001284 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001285
Damjan Marionf56b77a2016-10-03 19:44:57 +02001286 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001287 """
1288 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001289
Klement Sekeraf62ae122016-10-11 11:47:09 +02001290 :param test:
1291 :param err: error message
1292
1293 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001294 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001295
Damjan Marionf56b77a2016-10-03 19:44:57 +02001296 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001297 """
1298 Get test description
1299
1300 :param test:
1301 :returns: test description
1302
1303 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001304 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001305
Damjan Marionf56b77a2016-10-03 19:44:57 +02001306 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001307 """
1308 Start a test
1309
1310 :param test:
1311
1312 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001313
1314 def print_header(test):
1315 if not hasattr(test.__class__, '_header_printed'):
1316 print(double_line_delim)
1317 print(colorize(getdoc(test).splitlines()[0], GREEN))
1318 print(double_line_delim)
1319 test.__class__._header_printed = True
1320
1321 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001322
Damjan Marionf56b77a2016-10-03 19:44:57 +02001323 unittest.TestResult.startTest(self, test)
1324 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001325 self.stream.writeln(
1326 "Starting " + self.getDescription(test) + " ...")
1327 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001328
Damjan Marionf56b77a2016-10-03 19:44:57 +02001329 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001330 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001331 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001332
1333 :param test:
1334
1335 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001336 unittest.TestResult.stopTest(self, test)
1337 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001338 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001339 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001340 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001341 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001342 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001343 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001344 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001345
1346 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001347
Damjan Marionf56b77a2016-10-03 19:44:57 +02001348 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001349 """
1350 Print errors from running the test case
1351 """
juraj.linkesabec0122018-11-16 17:28:56 +01001352 if len(self.errors) > 0 or len(self.failures) > 0:
1353 self.stream.writeln()
1354 self.printErrorList('ERROR', self.errors)
1355 self.printErrorList('FAIL', self.failures)
1356
1357 # ^^ that is the last output from unittest before summary
1358 if not self.runner.print_summary:
1359 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1360 self.stream = devnull
1361 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001362
Damjan Marionf56b77a2016-10-03 19:44:57 +02001363 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001364 """
1365 Print error list to the output stream together with error type
1366 and test case description.
1367
1368 :param flavour: error type
1369 :param errors: iterable errors
1370
1371 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001372 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001373 self.stream.writeln(double_line_delim)
1374 self.stream.writeln("%s: %s" %
1375 (flavour, self.getDescription(test)))
1376 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001377 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001378
1379
Damjan Marionf56b77a2016-10-03 19:44:57 +02001380class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001381 """
Klement Sekera104543f2017-02-03 07:29:43 +01001382 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001383 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001384
Klement Sekeraf62ae122016-10-11 11:47:09 +02001385 @property
1386 def resultclass(self):
1387 """Class maintaining the results of the tests"""
1388 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001389
juraj.linkes184870a2018-07-16 14:22:01 +02001390 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001391 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001392 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001393 # ignore stream setting here, use hard-coded stdout to be in sync
1394 # with prints from VppTestCase methods ...
1395 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1396 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001397 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001398 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001399
juraj.linkesabec0122018-11-16 17:28:56 +01001400 self.orig_stream = self.stream
1401 self.resultclass.test_framework_result_pipe = result_pipe
1402
1403 self.print_summary = print_summary
1404
1405 def _makeResult(self):
1406 return self.resultclass(self.stream,
1407 self.descriptions,
1408 self.verbosity,
1409 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001410
Damjan Marionf56b77a2016-10-03 19:44:57 +02001411 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001412 """
1413 Run the tests
1414
1415 :param test:
1416
1417 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001418 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001419
1420 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001421 if not self.print_summary:
1422 self.stream = self.orig_stream
1423 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001424 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001425
1426
1427class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001428 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001429 self.logger = logger
1430 self.args = args
1431 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001432 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001433 super(Worker, self).__init__()
1434
1435 def run(self):
1436 executable = self.args[0]
1437 self.logger.debug("Running executable w/args `%s'" % self.args)
1438 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001439 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001440 env["CK_LOG_FILE_NAME"] = "-"
1441 self.process = subprocess.Popen(
1442 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1443 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1444 out, err = self.process.communicate()
1445 self.logger.debug("Finished running `%s'" % executable)
1446 self.logger.info("Return code is `%s'" % self.process.returncode)
1447 self.logger.info(single_line_delim)
1448 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1449 self.logger.info(single_line_delim)
1450 self.logger.info(out)
1451 self.logger.info(single_line_delim)
1452 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1453 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001454 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001455 self.logger.info(single_line_delim)
1456 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001457
1458if __name__ == '__main__':
1459 pass