blob: 00fee86e9b0bbaed950bb848e42b65d92185dce2 [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
Paul Vinciguerra496b0de2019-06-20 12:24:12 -04008import signal
Ole Trøan162989e2018-11-26 10:27:50 +00009import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +020010import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020011import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080012import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000013import random
14import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080015import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010016import platform
Ole Trøan162989e2018-11-26 10:27:50 +000017from collections import deque
18from threading import Thread, Event
19from inspect import getdoc, isclass
20from traceback import format_exception
21from logging import FileHandler, DEBUG, Formatter
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070022
23import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000024from scapy.packet import Raw
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040025import hook as hookmodule
Paul Vinciguerra919efad2018-12-17 21:43:43 -080026from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010027from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000028from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070029from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000030from vpp_papi_provider import VppPapiProvider
31from vpp_papi.vpp_stats import VPPStats
Paul Vinciguerra499ea642019-03-15 09:39:19 -070032from vpp_papi.vpp_transport_shmem import VppTransportShmemIOError
Ole Trøan162989e2018-11-26 10:27:50 +000033from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
34 get_logger, colorize
35from vpp_object import VppObjectRegistry
36from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020037from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
38from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
39from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080040
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010041if os.name == 'posix' and sys.version_info[0] < 3:
42 # using subprocess32 is recommended by python official documentation
43 # @ https://docs.python.org/2/library/subprocess.html
44 import subprocess32 as subprocess
45else:
46 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020047
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080048# Python2/3 compatible
49try:
50 input = raw_input
51except NameError:
52 pass
53
juraj.linkescae64f82018-09-19 15:01:47 +020054PASS = 0
55FAIL = 1
56ERROR = 2
57SKIP = 3
58TEST_RUN = 4
59
Klement Sekeraebbaf552018-02-17 13:41:33 +010060debug_framework = False
61if os.getenv('TEST_DEBUG', "0") == "1":
62 debug_framework = True
63 import debug_internal
64
Klement Sekeraf62ae122016-10-11 11:47:09 +020065"""
66 Test framework module.
67
68 The module provides a set of tools for constructing and running tests and
69 representing the results.
70"""
71
Klement Sekeraf62ae122016-10-11 11:47:09 +020072
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040073class VppDiedError(Exception):
74 """ exception for reporting that the subprocess has died."""
75
76 signals_by_value = {v: k for k, v in signal.__dict__.items() if
77 k.startswith('SIG') and not k.startswith('SIG_')}
78
79 def __init__(self, rv=None):
80 self.rv = rv
81 self.signal_name = None
82 try:
83 self.signal_name = VppDiedError.signals_by_value[-rv]
84 except KeyError:
85 pass
86
87 msg = "VPP subprocess died unexpectedly with return code: %d%s." % (
88 self.rv,
89 ' [%s]' % self.signal_name if
90 self.signal_name is not None else '')
91 super(VppDiedError, self).__init__(msg)
92
93
Damjan Marionf56b77a2016-10-03 19:44:57 +020094class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020095 """Private class to create packet info object.
96
97 Help process information about the next packet.
98 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020099 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100100 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200101 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100102 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200103 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100104 #: Store the index of the destination packet generator interface
105 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200106 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100107 #: Store expected ip version
108 ip = -1
109 #: Store expected upper protocol
110 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100111 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200112 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200113
Matej Klotton16a14cd2016-12-07 15:09:13 +0100114 def __eq__(self, other):
115 index = self.index == other.index
116 src = self.src == other.src
117 dst = self.dst == other.dst
118 data = self.data == other.data
119 return index and src and dst and data
120
Klement Sekeraf62ae122016-10-11 11:47:09 +0200121
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100122def pump_output(testclass):
123 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100124 stdout_fragment = ""
125 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400126 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100127 readable = select.select([testclass.vpp.stdout.fileno(),
128 testclass.vpp.stderr.fileno(),
129 testclass.pump_thread_wakeup_pipe[0]],
130 [], [])[0]
131 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100132 read = os.read(testclass.vpp.stdout.fileno(), 102400)
133 if len(read) > 0:
134 split = read.splitlines(True)
135 if len(stdout_fragment) > 0:
136 split[0] = "%s%s" % (stdout_fragment, split[0])
137 if len(split) > 0 and split[-1].endswith("\n"):
138 limit = None
139 else:
140 limit = -1
141 stdout_fragment = split[-1]
142 testclass.vpp_stdout_deque.extend(split[:limit])
143 if not testclass.cache_vpp_output:
144 for line in split[:limit]:
145 testclass.logger.debug(
146 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100147 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100148 read = os.read(testclass.vpp.stderr.fileno(), 102400)
149 if len(read) > 0:
150 split = read.splitlines(True)
151 if len(stderr_fragment) > 0:
152 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan7f991832018-12-06 17:35:12 +0100153 if len(split) > 0 and split[-1].endswith(b"\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100154 limit = None
155 else:
156 limit = -1
157 stderr_fragment = split[-1]
158 testclass.vpp_stderr_deque.extend(split[:limit])
159 if not testclass.cache_vpp_output:
160 for line in split[:limit]:
161 testclass.logger.debug(
162 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800163 # ignoring the dummy pipe here intentionally - the
164 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200165
166
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800167def _is_skip_aarch64_set():
juraj.linkes68ebc832018-11-29 09:37:08 +0100168 return os.getenv('SKIP_AARCH64', 'n').lower() in ('yes', 'y', '1')
169
Klement Sekera6aa58b72019-05-16 14:34:55 +0200170
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800171is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100172
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800173
174def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100175 return platform.machine() == 'aarch64'
176
Klement Sekera6aa58b72019-05-16 14:34:55 +0200177
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800178is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100179
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800180
181def _running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100182 s = os.getenv("EXTENDED_TESTS", "n")
183 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100184
Klement Sekera6aa58b72019-05-16 14:34:55 +0200185
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800186running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100187
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800188
189def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100190 os_id = os.getenv("OS_ID", "")
191 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200192
Klement Sekera6aa58b72019-05-16 14:34:55 +0200193
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800194running_on_centos = _running_on_centos
195
Klement Sekerad3e671e2017-09-29 12:36:37 +0200196
Klement Sekera909a6a12017-08-08 04:33:53 +0200197class KeepAliveReporter(object):
198 """
199 Singleton object which reports test start to parent process
200 """
201 _shared_state = {}
202
203 def __init__(self):
204 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800205 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200206
207 @property
208 def pipe(self):
209 return self._pipe
210
211 @pipe.setter
212 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800213 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200214 raise Exception("Internal error - pipe should only be set once.")
215 self._pipe = pipe
216
juraj.linkes40dd73b2018-09-21 13:55:16 +0200217 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200218 """
219 Write current test tmpdir & desc to keep-alive pipe to signal liveness
220 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200221 if self.pipe is None:
222 # if not running forked..
223 return
224
Klement Sekera909a6a12017-08-08 04:33:53 +0200225 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200226 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200227 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200228 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200229
Dave Wallacee2efd122017-09-30 22:04:21 -0400230 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200231
232
Damjan Marionf56b77a2016-10-03 19:44:57 +0200233class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100234 """This subclass is a base class for VPP test cases that are implemented as
235 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200236 """
237
Ole Troana45dc072018-12-21 16:04:22 +0100238 extra_vpp_punt_config = []
239 extra_vpp_plugin_config = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100240
Klement Sekeraf62ae122016-10-11 11:47:09 +0200241 @property
242 def packet_infos(self):
243 """List of packet infos"""
244 return self._packet_infos
245
Klement Sekeradab231a2016-12-21 08:50:14 +0100246 @classmethod
247 def get_packet_count_for_if_idx(cls, dst_if_index):
248 """Get the number of packet info for specified destination if index"""
249 if dst_if_index in cls._packet_count_for_dst_if_idx:
250 return cls._packet_count_for_dst_if_idx[dst_if_index]
251 else:
252 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200253
254 @classmethod
255 def instance(cls):
256 """Return the instance of this testcase"""
257 return cls.test_instance
258
Damjan Marionf56b77a2016-10-03 19:44:57 +0200259 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200260 def set_debug_flags(cls, d):
261 cls.debug_core = False
262 cls.debug_gdb = False
263 cls.debug_gdbserver = False
264 if d is None:
265 return
266 dl = d.lower()
267 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200268 cls.debug_core = True
269 elif dl == "gdb":
270 cls.debug_gdb = True
271 elif dl == "gdbserver":
272 cls.debug_gdbserver = True
273 else:
274 raise Exception("Unrecognized DEBUG option: '%s'" % d)
275
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800276 @staticmethod
277 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200278 cpu_usage_list = [set(range(psutil.cpu_count()))]
279 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
280 if 'vpp_main' == p.info['name']]
281 for vpp_process in vpp_processes:
282 for cpu_usage_set in cpu_usage_list:
283 try:
284 cpu_num = vpp_process.cpu_num()
285 if cpu_num in cpu_usage_set:
286 cpu_usage_set_index = cpu_usage_list.index(
287 cpu_usage_set)
288 if cpu_usage_set_index == len(cpu_usage_list) - 1:
289 cpu_usage_list.append({cpu_num})
290 else:
291 cpu_usage_list[cpu_usage_set_index + 1].add(
292 cpu_num)
293 cpu_usage_set.remove(cpu_num)
294 break
295 except psutil.NoSuchProcess:
296 pass
297
298 for cpu_usage_set in cpu_usage_list:
299 if len(cpu_usage_set) > 0:
300 min_usage_set = cpu_usage_set
301 break
302
303 return random.choice(tuple(min_usage_set))
304
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800305 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200306 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200307 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100308 s = os.getenv("STEP", "n")
309 cls.step = True if s.lower() in ("y", "yes", "1") else False
310 d = os.getenv("DEBUG", None)
311 c = os.getenv("CACHE_OUTPUT", "1")
312 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200313 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100314 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
315 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400316 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100317 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
318 plugin_path = None
319 if cls.plugin_path is not None:
320 if cls.extern_plugin_path is not None:
321 plugin_path = "%s:%s" % (
322 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100323 else:
324 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100325 elif cls.extern_plugin_path is not None:
326 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100327 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100328 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100329 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100330 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100331 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100332 if size is not None:
333 coredump_size = "coredump-size %s" % size
334 if coredump_size is None:
335 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200336
Ole Troana45dc072018-12-21 16:04:22 +0100337 cpu_core_number = cls.get_least_used_cpu()
juraj.linkes184870a2018-07-16 14:22:01 +0200338
Ole Troana45dc072018-12-21 16:04:22 +0100339 cls.vpp_cmdline = [cls.vpp_bin, "unix",
340 "{", "nodaemon", debug_cli, "full-coredump",
341 coredump_size, "runtime-dir", cls.tempdir, "}",
342 "api-trace", "{", "on", "}", "api-segment", "{",
343 "prefix", cls.shm_prefix, "}", "cpu", "{",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200344 "main-core", str(cpu_core_number), "}",
345 "statseg", "{", "socket-name", cls.stats_sock, "}",
346 "socksvr", "{", "socket-name", cls.api_sock, "}",
347 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100348 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200349 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100350 "}", "plugin", "unittest_plugin.so", "{", "enable",
351 "}"] + cls.extra_vpp_plugin_config + ["}", ]
352 if cls.extra_vpp_punt_config is not None:
353 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100354 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100355 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400356 if cls.test_plugin_path is not None:
357 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
358
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100359 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
360 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200361
362 @classmethod
363 def wait_for_enter(cls):
364 if cls.debug_gdbserver:
365 print(double_line_delim)
366 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
367 elif cls.debug_gdb:
368 print(double_line_delim)
369 print("Spawned VPP with PID: %d" % cls.vpp.pid)
370 else:
371 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
372 return
373 print(single_line_delim)
374 print("You can debug the VPP using e.g.:")
375 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400376 print("sudo gdb " + cls.vpp_bin +
377 " -ex 'target remote localhost:7777'")
Klement Sekera277b89c2016-10-28 13:20:27 +0200378 print("Now is the time to attach a gdb by running the above "
379 "command, set up breakpoints etc. and then resume VPP from "
380 "within gdb by issuing the 'continue' command")
381 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400382 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Klement Sekera277b89c2016-10-28 13:20:27 +0200383 print("Now is the time to attach a gdb by running the above "
384 "command and set up breakpoints etc.")
385 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800386 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200387
388 @classmethod
389 def run_vpp(cls):
390 cmdline = cls.vpp_cmdline
391
392 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100393 gdbserver = '/usr/bin/gdbserver'
394 if not os.path.isfile(gdbserver) or \
395 not os.access(gdbserver, os.X_OK):
396 raise Exception("gdbserver binary '%s' does not exist or is "
397 "not executable" % gdbserver)
398
399 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200400 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
401
Klement Sekera931be3a2016-11-03 05:36:01 +0100402 try:
403 cls.vpp = subprocess.Popen(cmdline,
404 stdout=subprocess.PIPE,
405 stderr=subprocess.PIPE,
406 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800407 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800408 cls.logger.critical("Subprocess returned with non-0 return code: ("
409 "%s)", e.returncode)
410 raise
411 except OSError as e:
412 cls.logger.critical("Subprocess returned with OS error: "
413 "(%s) %s", e.errno, e.strerror)
414 raise
415 except Exception as e:
416 cls.logger.exception("Subprocess returned unexpected from "
417 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100418 raise
419
Klement Sekera277b89c2016-10-28 13:20:27 +0200420 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100421
Damjan Marionf56b77a2016-10-03 19:44:57 +0200422 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200423 def wait_for_stats_socket(cls):
424 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800425 ok = False
426 while time.time() < deadline or \
427 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200428 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800429 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200430 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700431 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800432 if not ok:
433 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200434
435 @classmethod
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400436 def wait_for_coredump(cls):
437 corefile = cls.tempdir + "/core"
438 if os.path.isfile(corefile):
439 cls.logger.error("Waiting for coredump to complete: %s", corefile)
440 curr_size = os.path.getsize(corefile)
441 deadline = time.time() + 60
442 ok = False
443 while time.time() < deadline:
444 cls.sleep(1)
445 size = curr_size
446 curr_size = os.path.getsize(corefile)
447 if size == curr_size:
448 ok = True
449 break
450 if not ok:
451 cls.logger.error("Timed out waiting for coredump to complete:"
452 " %s", corefile)
453 else:
454 cls.logger.error("Coredump complete: %s, size %d",
455 corefile, curr_size)
456
457 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200458 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200459 """
460 Perform class setup before running the testcase
461 Remove shared memory files, start vpp and connect the vpp-api
462 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800463 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100464 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100465 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100466 cls.logger = get_logger(cls.__name__)
467 if hasattr(cls, 'parallel_handler'):
468 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100469 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700470
Klement Sekeraf62ae122016-10-11 11:47:09 +0200471 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200472 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200473 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200474 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200475 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
476 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100477 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
478 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200479 cls.file_handler.setLevel(DEBUG)
480 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700481 cls.logger.debug("--- setUpClass() for %s called ---" %
482 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200483 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200484 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200485 cls.logger.info("Temporary dir is %s, shm prefix is %s",
486 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200487 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100488 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100489 cls._captures = []
490 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200491 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100492 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100493 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200494 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200495 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200496 # need to catch exceptions here because if we raise, then the cleanup
497 # doesn't get called and we might end with a zombie vpp
498 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200499 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200500 cls.reporter.send_keep_alive(cls, 'setUpClass')
501 VppTestResult.current_test_case_info = TestCaseInfo(
502 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100503 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100504 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100505 cls.pump_thread_stop_flag = Event()
506 cls.pump_thread_wakeup_pipe = os.pipe()
507 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100508 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100509 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200510 if cls.debug_gdb or cls.debug_gdbserver:
511 read_timeout = 0
512 else:
513 read_timeout = 5
514 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
515 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100516 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400517 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100518 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400519 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100520 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200521 cls.wait_for_stats_socket()
522 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200523 try:
524 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100525 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200526 cls.vpp_startup_failed = True
527 cls.logger.critical(
528 "VPP died shortly after startup, check the"
529 " output to standard error for possible cause")
530 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100531 try:
532 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100533 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100534 try:
535 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100536 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100537 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100538 if cls.debug_gdbserver:
539 print(colorize("You're running VPP inside gdbserver but "
540 "VPP-API connection failed, did you forget "
541 "to 'continue' VPP from within gdb?", RED))
542 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100543 except Exception:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400544
545 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100546 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200547
Damjan Marionf56b77a2016-10-03 19:44:57 +0200548 @classmethod
549 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200550 """
551 Disconnect vpp-api, kill vpp and cleanup shared memory files
552 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200553 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
554 cls.vpp.poll()
555 if cls.vpp.returncode is None:
556 print(double_line_delim)
557 print("VPP or GDB server is still running")
558 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800559 input("When done debugging, press ENTER to kill the "
560 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200561
juraj.linkes184870a2018-07-16 14:22:01 +0200562 # first signal that we want to stop the pump thread, then wake it up
563 if hasattr(cls, 'pump_thread_stop_flag'):
564 cls.pump_thread_stop_flag.set()
565 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100566 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100567 if hasattr(cls, 'pump_thread'):
568 cls.logger.debug("Waiting for pump thread to stop")
569 cls.pump_thread.join()
570 if hasattr(cls, 'vpp_stderr_reader_thread'):
571 cls.logger.debug("Waiting for stdderr pump to stop")
572 cls.vpp_stderr_reader_thread.join()
573
Klement Sekeraf62ae122016-10-11 11:47:09 +0200574 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100575 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700576 cls.logger.debug("Disconnecting class vapi client on %s",
577 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100578 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700579 cls.logger.debug("Deleting class vapi attribute on %s",
580 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100581 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200582 cls.vpp.poll()
583 if cls.vpp.returncode is None:
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400584 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100585 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400586 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100587 cls.logger.debug("Waiting for vpp to die")
588 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700589 cls.logger.debug("Deleting class vpp attribute on %s",
590 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200591 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200592
Klement Sekera3747c752017-04-10 06:30:17 +0200593 if cls.vpp_startup_failed:
594 stdout_log = cls.logger.info
595 stderr_log = cls.logger.critical
596 else:
597 stdout_log = cls.logger.info
598 stderr_log = cls.logger.info
599
Klement Sekerae4504c62016-12-08 10:16:41 +0100600 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200601 stdout_log(single_line_delim)
602 stdout_log('VPP output to stdout while running %s:', cls.__name__)
603 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100604 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200605 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
606 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200607 stdout_log('\n%s', vpp_output)
608 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200609
Klement Sekerae4504c62016-12-08 10:16:41 +0100610 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200611 stderr_log(single_line_delim)
612 stderr_log('VPP output to stderr while running %s:', cls.__name__)
613 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100614 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200615 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
616 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200617 stderr_log('\n%s', vpp_output)
618 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200619
Damjan Marionf56b77a2016-10-03 19:44:57 +0200620 @classmethod
621 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200622 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700623 cls.logger.debug("--- tearDownClass() for %s called ---" %
624 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200625 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200626 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200627 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100628 cls.reset_packet_infos()
629 if debug_framework:
630 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200631
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700632 def show_commands_at_teardown(self):
633 """ Allow subclass specific teardown logging additions."""
634 self.logger.info("--- No test specific show commands provided. ---")
635
Damjan Marionf56b77a2016-10-03 19:44:57 +0200636 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200637 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100638 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
639 (self.__class__.__name__, self._testMethodName,
640 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700641
642 try:
643 if not self.vpp_dead:
644 self.logger.debug(self.vapi.cli("show trace max 1000"))
645 self.logger.info(self.vapi.ppcli("show interface"))
646 self.logger.info(self.vapi.ppcli("show hardware"))
647 self.logger.info(self.statistics.set_errors_str())
648 self.logger.info(self.vapi.ppcli("show run"))
649 self.logger.info(self.vapi.ppcli("show log"))
650 self.logger.info("Logging testcase specific show commands.")
651 self.show_commands_at_teardown()
652 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500653 # Save/Dump VPP api trace log
654 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
655 tmp_api_trace = "/tmp/%s" % api_trace
656 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
657 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
658 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
659 vpp_api_trace_log))
660 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500661 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500662 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700663 except VppTransportShmemIOError:
664 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
665 "Cannot log show commands.")
666 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100667 else:
668 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200669
Damjan Marionf56b77a2016-10-03 19:44:57 +0200670 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200671 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800672 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200673 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100674 if self.vpp_dead:
675 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100676 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100677 self.vpp_stdout_deque.append(
678 "--- test setUp() for %s.%s(%s) starts here ---\n" %
679 (self.__class__.__name__, self._testMethodName,
680 self._testMethodDoc))
681 self.vpp_stderr_deque.append(
682 "--- test setUp() for %s.%s(%s) starts here ---\n" %
683 (self.__class__.__name__, self._testMethodName,
684 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200685 self.vapi.cli("clear trace")
686 # store the test instance inside the test class - so that objects
687 # holding the class can access instance methods (like assertEqual)
688 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200689
Damjan Marionf56b77a2016-10-03 19:44:57 +0200690 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200691 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200692 """
693 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200694
Klement Sekera75e7d132017-09-20 08:26:30 +0200695 :param interfaces: iterable interface indexes (if None,
696 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200697
Klement Sekeraf62ae122016-10-11 11:47:09 +0200698 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200699 if interfaces is None:
700 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200701 for i in interfaces:
702 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200703
Damjan Marionf56b77a2016-10-03 19:44:57 +0200704 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100705 def register_capture(cls, cap_name):
706 """ Register a capture in the testclass """
707 # add to the list of captures with current timestamp
708 cls._captures.append((time.time(), cap_name))
709 # filter out from zombies
710 cls._zombie_captures = [(stamp, name)
711 for (stamp, name) in cls._zombie_captures
712 if name != cap_name]
713
714 @classmethod
715 def pg_start(cls):
716 """ Remove any zombie captures and enable the packet generator """
717 # how long before capture is allowed to be deleted - otherwise vpp
718 # crashes - 100ms seems enough (this shouldn't be needed at all)
719 capture_ttl = 0.1
720 now = time.time()
721 for stamp, cap_name in cls._zombie_captures:
722 wait = stamp + capture_ttl - now
723 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100724 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100725 now = time.time()
726 cls.logger.debug("Removing zombie capture %s" % cap_name)
727 cls.vapi.cli('packet-generator delete %s' % cap_name)
728
Klement Sekerad91fa612019-01-15 13:25:09 +0100729 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200730 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100731 cls._zombie_captures = cls._captures
732 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200733
Damjan Marionf56b77a2016-10-03 19:44:57 +0200734 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200735 def create_pg_interfaces(cls, interfaces):
736 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100737 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200738
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100739 :param interfaces: iterable indexes of the interfaces.
740 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200741
Klement Sekeraf62ae122016-10-11 11:47:09 +0200742 """
743 result = []
744 for i in interfaces:
745 intf = VppPGInterface(cls, i)
746 setattr(cls, intf.name, intf)
747 result.append(intf)
748 cls.pg_interfaces = result
749 return result
750
Matej Klotton0178d522016-11-04 11:11:44 +0100751 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200752 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100753 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100754 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100755
Klement Sekerab9ef2732018-06-24 22:49:33 +0200756 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100757 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100758 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200759 result = [VppLoInterface(cls) for i in range(count)]
760 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100761 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100762 cls.lo_interfaces = result
763 return result
764
Neale Ranns192b13f2019-03-15 02:16:20 -0700765 @classmethod
766 def create_bvi_interfaces(cls, count):
767 """
768 Create BVI interfaces.
769
770 :param count: number of interfaces created.
771 :returns: List of created interfaces.
772 """
773 result = [VppBviInterface(cls) for i in range(count)]
774 for intf in result:
775 setattr(cls, intf.name, intf)
776 cls.bvi_interfaces = result
777 return result
778
Damjan Marionf56b77a2016-10-03 19:44:57 +0200779 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200780 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200781 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200782 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200783 NOTE: Currently works only when Raw layer is present.
784
785 :param packet: packet
786 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200787 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200788
789 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200790 packet_len = len(packet) + 4
791 extend = size - packet_len
792 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200793 num = (extend // len(padding)) + 1
794 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200795
Klement Sekeradab231a2016-12-21 08:50:14 +0100796 @classmethod
797 def reset_packet_infos(cls):
798 """ Reset the list of packet info objects and packet counts to zero """
799 cls._packet_infos = {}
800 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200801
Klement Sekeradab231a2016-12-21 08:50:14 +0100802 @classmethod
803 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200804 """
805 Create packet info object containing the source and destination indexes
806 and add it to the testcase's packet info list
807
Klement Sekeradab231a2016-12-21 08:50:14 +0100808 :param VppInterface src_if: source interface
809 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200810
811 :returns: _PacketInfo object
812
813 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200814 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100815 info.index = len(cls._packet_infos)
816 info.src = src_if.sw_if_index
817 info.dst = dst_if.sw_if_index
818 if isinstance(dst_if, VppSubInterface):
819 dst_idx = dst_if.parent.sw_if_index
820 else:
821 dst_idx = dst_if.sw_if_index
822 if dst_idx in cls._packet_count_for_dst_if_idx:
823 cls._packet_count_for_dst_if_idx[dst_idx] += 1
824 else:
825 cls._packet_count_for_dst_if_idx[dst_idx] = 1
826 cls._packet_infos[info.index] = info
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 @staticmethod
830 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200831 """
832 Convert _PacketInfo object to packet payload
833
834 :param info: _PacketInfo object
835
836 :returns: string containing serialized data from packet info
837 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100838 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
839 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200840
Damjan Marionf56b77a2016-10-03 19:44:57 +0200841 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800842 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200843 """
844 Convert packet payload to _PacketInfo object
845
846 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700847 :type payload: <class 'scapy.packet.Raw'>
848 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800849 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700850 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200851 :returns: _PacketInfo object containing de-serialized data from payload
852
853 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800854 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200855 info = _PacketInfo()
856 info.index = int(numbers[0])
857 info.src = int(numbers[1])
858 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100859 info.ip = int(numbers[3])
860 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200861 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200862
Damjan Marionf56b77a2016-10-03 19:44:57 +0200863 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200864 """
865 Iterate over the packet info list stored in the testcase
866 Start iteration with first element if info is None
867 Continue based on index in info if info is specified
868
869 :param info: info or None
870 :returns: next info in list or None if no more infos
871 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200872 if info is None:
873 next_index = 0
874 else:
875 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100876 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200877 return None
878 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100879 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200880
Klement Sekeraf62ae122016-10-11 11:47:09 +0200881 def get_next_packet_info_for_interface(self, src_index, info):
882 """
883 Search the packet info list for the next packet info with same source
884 interface index
885
886 :param src_index: source interface index to search for
887 :param info: packet info - where to start the search
888 :returns: packet info or None
889
890 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200891 while True:
892 info = self.get_next_packet_info(info)
893 if info is None:
894 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200895 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200896 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200897
Klement Sekeraf62ae122016-10-11 11:47:09 +0200898 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
899 """
900 Search the packet info list for the next packet info with same source
901 and destination interface indexes
902
903 :param src_index: source interface index to search for
904 :param dst_index: destination interface index to search for
905 :param info: packet info - where to start the search
906 :returns: packet info or None
907
908 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200909 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200910 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200911 if info is None:
912 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200913 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200914 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200915
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200916 def assert_equal(self, real_value, expected_value, name_or_class=None):
917 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100918 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200919 return
920 try:
921 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
922 msg = msg % (getdoc(name_or_class).strip(),
923 real_value, str(name_or_class(real_value)),
924 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100925 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200926 msg = "Invalid %s: %s does not match expected value %s" % (
927 name_or_class, real_value, expected_value)
928
929 self.assertEqual(real_value, expected_value, msg)
930
Klement Sekerab17dd962017-01-09 07:43:48 +0100931 def assert_in_range(self,
932 real_value,
933 expected_min,
934 expected_max,
935 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200936 if name is None:
937 msg = None
938 else:
939 msg = "Invalid %s: %s out of range <%s,%s>" % (
940 name, real_value, expected_min, expected_max)
941 self.assertTrue(expected_min <= real_value <= expected_max, msg)
942
Klement Sekerad81ae412018-05-16 10:52:54 +0200943 def assert_packet_checksums_valid(self, packet,
944 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700945 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200946 self.logger.debug(
947 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200948 udp_layers = ['UDP', 'UDPerror']
949 checksum_fields = ['cksum', 'chksum']
950 checksums = []
951 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700952 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200953 while True:
954 layer = temp.getlayer(counter)
955 if layer:
956 for cf in checksum_fields:
957 if hasattr(layer, cf):
958 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +0200959 0 == getattr(layer, cf) and \
960 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200961 continue
962 delattr(layer, cf)
963 checksums.append((counter, cf))
964 else:
965 break
966 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200967 if 0 == len(checksums):
968 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700969 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200970 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200971 calc_sum = getattr(temp[layer], cf)
972 self.assert_equal(
973 getattr(received[layer], cf), calc_sum,
974 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
975 self.logger.debug(
976 "Checksum field `%s` on `%s` layer has correct value `%s`" %
977 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200978
979 def assert_checksum_valid(self, received_packet, layer,
980 field_name='chksum',
981 ignore_zero_checksum=False):
982 """ Check checksum of received packet on given layer """
983 received_packet_checksum = getattr(received_packet[layer], field_name)
984 if ignore_zero_checksum and 0 == received_packet_checksum:
985 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700986 recalculated = received_packet.__class__(
987 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200988 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700989 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +0200990 self.assert_equal(received_packet_checksum,
991 getattr(recalculated[layer], field_name),
992 "packet checksum on layer: %s" % layer)
993
994 def assert_ip_checksum_valid(self, received_packet,
995 ignore_zero_checksum=False):
996 self.assert_checksum_valid(received_packet, 'IP',
997 ignore_zero_checksum=ignore_zero_checksum)
998
999 def assert_tcp_checksum_valid(self, received_packet,
1000 ignore_zero_checksum=False):
1001 self.assert_checksum_valid(received_packet, 'TCP',
1002 ignore_zero_checksum=ignore_zero_checksum)
1003
1004 def assert_udp_checksum_valid(self, received_packet,
1005 ignore_zero_checksum=True):
1006 self.assert_checksum_valid(received_packet, 'UDP',
1007 ignore_zero_checksum=ignore_zero_checksum)
1008
1009 def assert_embedded_icmp_checksum_valid(self, received_packet):
1010 if received_packet.haslayer(IPerror):
1011 self.assert_checksum_valid(received_packet, 'IPerror')
1012 if received_packet.haslayer(TCPerror):
1013 self.assert_checksum_valid(received_packet, 'TCPerror')
1014 if received_packet.haslayer(UDPerror):
1015 self.assert_checksum_valid(received_packet, 'UDPerror',
1016 ignore_zero_checksum=True)
1017 if received_packet.haslayer(ICMPerror):
1018 self.assert_checksum_valid(received_packet, 'ICMPerror')
1019
1020 def assert_icmp_checksum_valid(self, received_packet):
1021 self.assert_checksum_valid(received_packet, 'ICMP')
1022 self.assert_embedded_icmp_checksum_valid(received_packet)
1023
1024 def assert_icmpv6_checksum_valid(self, pkt):
1025 if pkt.haslayer(ICMPv6DestUnreach):
1026 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1027 self.assert_embedded_icmp_checksum_valid(pkt)
1028 if pkt.haslayer(ICMPv6EchoRequest):
1029 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1030 if pkt.haslayer(ICMPv6EchoReply):
1031 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1032
Klement Sekera3a343d42019-05-16 14:35:46 +02001033 def get_packet_counter(self, counter):
1034 if counter.startswith("/"):
1035 counter_value = self.statistics.get_counter(counter)
1036 else:
1037 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001038 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001039 for i in range(1, len(counters) - 1):
1040 results = counters[i].split()
1041 if results[1] == counter:
1042 counter_value = int(results[0])
1043 break
1044 return counter_value
1045
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001046 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001047 counter_value = self.get_packet_counter(counter)
1048 self.assert_equal(counter_value, expected_value,
1049 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001050
Ole Troan233e4682019-05-16 15:01:34 +02001051 def assert_error_counter_equal(self, counter, expected_value):
1052 counter_value = self.statistics.get_err_counter(counter)
1053 self.assert_equal(counter_value, expected_value,
1054 "error counter `%s'" % counter)
1055
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001056 @classmethod
1057 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001058
1059 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1060 # * by Guido, only the main thread can be interrupted.
1061 # */
1062 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1063 if timeout == 0:
1064 # yield quantum
1065 if hasattr(os, 'sched_yield'):
1066 os.sched_yield()
1067 else:
1068 time.sleep(0)
1069 return
1070
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001071 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001072 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001073 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001074 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001075 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001076 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001077 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001078 "slept for %es instead of ~%es!",
1079 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001080 if hasattr(cls, 'logger'):
1081 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001082 "Finished sleep (%s) - slept %es (wanted %es)",
1083 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001084
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001085 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001086 self.vapi.cli("clear trace")
1087 intf.add_stream(pkts)
1088 self.pg_enable_capture(self.pg_interfaces)
1089 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001090
1091 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1092 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001093 if not timeout:
1094 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001095 for i in self.pg_interfaces:
1096 i.get_capture(0, timeout=timeout)
1097 i.assert_nothing_captured(remark=remark)
1098 timeout = 0.1
1099
Neale Rannsd7603d92019-03-28 08:56:10 +00001100 def send_and_expect(self, intf, pkts, output, n_rx=None):
1101 if not n_rx:
1102 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001103 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001104 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001105 return rx
1106
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001107 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1108 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001109 rx = output.get_capture(len(pkts))
1110 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001111 if not timeout:
1112 timeout = 1
1113 for i in self.pg_interfaces:
1114 if i not in outputs:
1115 i.get_capture(0, timeout=timeout)
1116 i.assert_nothing_captured()
1117 timeout = 0.1
1118
Neale Ranns52fae862018-01-08 04:41:42 -08001119 return rx
1120
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001121 def runTest(self):
1122 """ unittest calls runTest when TestCase is instantiated without a
1123 test case. Use case: Writing unittests against VppTestCase"""
1124 pass
1125
Damjan Marionf56b77a2016-10-03 19:44:57 +02001126
juraj.linkes184870a2018-07-16 14:22:01 +02001127def get_testcase_doc_name(test):
1128 return getdoc(test.__class__).splitlines()[0]
1129
1130
Ole Trøan5ba91592018-11-22 10:01:09 +00001131def get_test_description(descriptions, test):
1132 short_description = test.shortDescription()
1133 if descriptions and short_description:
1134 return short_description
1135 else:
1136 return str(test)
1137
1138
juraj.linkes40dd73b2018-09-21 13:55:16 +02001139class TestCaseInfo(object):
1140 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1141 self.logger = logger
1142 self.tempdir = tempdir
1143 self.vpp_pid = vpp_pid
1144 self.vpp_bin_path = vpp_bin_path
1145 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001146
1147
Damjan Marionf56b77a2016-10-03 19:44:57 +02001148class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001149 """
1150 @property result_string
1151 String variable to store the test case result string.
1152 @property errors
1153 List variable containing 2-tuples of TestCase instances and strings
1154 holding formatted tracebacks. Each tuple represents a test which
1155 raised an unexpected exception.
1156 @property failures
1157 List variable containing 2-tuples of TestCase instances and strings
1158 holding formatted tracebacks. Each tuple represents a test where
1159 a failure was explicitly signalled using the TestCase.assert*()
1160 methods.
1161 """
1162
juraj.linkes40dd73b2018-09-21 13:55:16 +02001163 failed_test_cases_info = set()
1164 core_crash_test_cases_info = set()
1165 current_test_case_info = None
1166
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001167 def __init__(self, stream=None, descriptions=None, verbosity=None,
1168 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001169 """
Klement Sekerada505f62017-01-04 12:58:53 +01001170 :param stream File descriptor to store where to report test results.
1171 Set to the standard error stream by default.
1172 :param descriptions Boolean variable to store information if to use
1173 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001174 :param verbosity Integer variable to store required verbosity level.
1175 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001176 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001177 self.stream = stream
1178 self.descriptions = descriptions
1179 self.verbosity = verbosity
1180 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001181 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001182
Damjan Marionf56b77a2016-10-03 19:44:57 +02001183 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001184 """
1185 Record a test succeeded result
1186
1187 :param test:
1188
1189 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001190 if self.current_test_case_info:
1191 self.current_test_case_info.logger.debug(
1192 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1193 test._testMethodName,
1194 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001195 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001196 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001197
juraj.linkescae64f82018-09-19 15:01:47 +02001198 self.send_result_through_pipe(test, PASS)
1199
Klement Sekeraf62ae122016-10-11 11:47:09 +02001200 def addSkip(self, test, reason):
1201 """
1202 Record a test skipped.
1203
1204 :param test:
1205 :param reason:
1206
1207 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001208 if self.current_test_case_info:
1209 self.current_test_case_info.logger.debug(
1210 "--- addSkip() %s.%s(%s) called, reason is %s" %
1211 (test.__class__.__name__, test._testMethodName,
1212 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001213 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001214 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001215
juraj.linkescae64f82018-09-19 15:01:47 +02001216 self.send_result_through_pipe(test, SKIP)
1217
juraj.linkes40dd73b2018-09-21 13:55:16 +02001218 def symlink_failed(self):
1219 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001220 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001221 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001222 link_path = os.path.join(
1223 failed_dir,
1224 '%s-FAILED' %
1225 os.path.basename(self.current_test_case_info.tempdir))
1226 if self.current_test_case_info.logger:
1227 self.current_test_case_info.logger.debug(
1228 "creating a link to the failed test")
1229 self.current_test_case_info.logger.debug(
1230 "os.symlink(%s, %s)" %
1231 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001232 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001233 if self.current_test_case_info.logger:
1234 self.current_test_case_info.logger.debug(
1235 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001236 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001237 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001238
Klement Sekeraf413bef2017-08-15 07:09:02 +02001239 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001240 if self.current_test_case_info.logger:
1241 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001242
juraj.linkescae64f82018-09-19 15:01:47 +02001243 def send_result_through_pipe(self, test, result):
1244 if hasattr(self, 'test_framework_result_pipe'):
1245 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001246 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001247 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001248
juraj.linkes40dd73b2018-09-21 13:55:16 +02001249 def log_error(self, test, err, fn_name):
1250 if self.current_test_case_info:
1251 if isinstance(test, unittest.suite._ErrorHolder):
1252 test_name = test.description
1253 else:
1254 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1255 test._testMethodName,
1256 test._testMethodDoc)
1257 self.current_test_case_info.logger.debug(
1258 "--- %s() %s called, err is %s" %
1259 (fn_name, test_name, err))
1260 self.current_test_case_info.logger.debug(
1261 "formatted exception is:\n%s" %
1262 "".join(format_exception(*err)))
1263
1264 def add_error(self, test, err, unittest_fn, error_type):
1265 if error_type == FAIL:
1266 self.log_error(test, err, 'addFailure')
1267 error_type_str = colorize("FAIL", RED)
1268 elif error_type == ERROR:
1269 self.log_error(test, err, 'addError')
1270 error_type_str = colorize("ERROR", RED)
1271 else:
1272 raise Exception('Error type %s cannot be used to record an '
1273 'error or a failure' % error_type)
1274
1275 unittest_fn(self, test, err)
1276 if self.current_test_case_info:
1277 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1278 (error_type_str,
1279 self.current_test_case_info.tempdir)
1280 self.symlink_failed()
1281 self.failed_test_cases_info.add(self.current_test_case_info)
1282 if is_core_present(self.current_test_case_info.tempdir):
1283 if not self.current_test_case_info.core_crash_test:
1284 if isinstance(test, unittest.suite._ErrorHolder):
1285 test_name = str(test)
1286 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001287 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001288 get_testcase_doc_name(test), test.id())
1289 self.current_test_case_info.core_crash_test = test_name
1290 self.core_crash_test_cases_info.add(
1291 self.current_test_case_info)
1292 else:
1293 self.result_string = '%s [no temp dir]' % error_type_str
1294
1295 self.send_result_through_pipe(test, error_type)
1296
Damjan Marionf56b77a2016-10-03 19:44:57 +02001297 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001298 """
1299 Record a test failed result
1300
1301 :param test:
1302 :param err: error message
1303
1304 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001305 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001306
Damjan Marionf56b77a2016-10-03 19:44:57 +02001307 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001308 """
1309 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001310
Klement Sekeraf62ae122016-10-11 11:47:09 +02001311 :param test:
1312 :param err: error message
1313
1314 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001315 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001316
Damjan Marionf56b77a2016-10-03 19:44:57 +02001317 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001318 """
1319 Get test description
1320
1321 :param test:
1322 :returns: test description
1323
1324 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001325 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001326
Damjan Marionf56b77a2016-10-03 19:44:57 +02001327 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001328 """
1329 Start a test
1330
1331 :param test:
1332
1333 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001334
1335 def print_header(test):
1336 if not hasattr(test.__class__, '_header_printed'):
1337 print(double_line_delim)
1338 print(colorize(getdoc(test).splitlines()[0], GREEN))
1339 print(double_line_delim)
1340 test.__class__._header_printed = True
1341
1342 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001343
Damjan Marionf56b77a2016-10-03 19:44:57 +02001344 unittest.TestResult.startTest(self, test)
1345 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001346 self.stream.writeln(
1347 "Starting " + self.getDescription(test) + " ...")
1348 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001349
Damjan Marionf56b77a2016-10-03 19:44:57 +02001350 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001351 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001352 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001353
1354 :param test:
1355
1356 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001357 unittest.TestResult.stopTest(self, test)
1358 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001359 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001360 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001361 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001362 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001363 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001364 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001365 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001366
1367 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001368
Damjan Marionf56b77a2016-10-03 19:44:57 +02001369 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001370 """
1371 Print errors from running the test case
1372 """
juraj.linkesabec0122018-11-16 17:28:56 +01001373 if len(self.errors) > 0 or len(self.failures) > 0:
1374 self.stream.writeln()
1375 self.printErrorList('ERROR', self.errors)
1376 self.printErrorList('FAIL', self.failures)
1377
1378 # ^^ that is the last output from unittest before summary
1379 if not self.runner.print_summary:
1380 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1381 self.stream = devnull
1382 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001383
Damjan Marionf56b77a2016-10-03 19:44:57 +02001384 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001385 """
1386 Print error list to the output stream together with error type
1387 and test case description.
1388
1389 :param flavour: error type
1390 :param errors: iterable errors
1391
1392 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001393 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001394 self.stream.writeln(double_line_delim)
1395 self.stream.writeln("%s: %s" %
1396 (flavour, self.getDescription(test)))
1397 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001398 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001399
1400
Damjan Marionf56b77a2016-10-03 19:44:57 +02001401class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001402 """
Klement Sekera104543f2017-02-03 07:29:43 +01001403 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001404 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001405
Klement Sekeraf62ae122016-10-11 11:47:09 +02001406 @property
1407 def resultclass(self):
1408 """Class maintaining the results of the tests"""
1409 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001410
juraj.linkes184870a2018-07-16 14:22:01 +02001411 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001412 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001413 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001414 # ignore stream setting here, use hard-coded stdout to be in sync
1415 # with prints from VppTestCase methods ...
1416 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1417 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001418 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001419 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001420
juraj.linkesabec0122018-11-16 17:28:56 +01001421 self.orig_stream = self.stream
1422 self.resultclass.test_framework_result_pipe = result_pipe
1423
1424 self.print_summary = print_summary
1425
1426 def _makeResult(self):
1427 return self.resultclass(self.stream,
1428 self.descriptions,
1429 self.verbosity,
1430 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001431
Damjan Marionf56b77a2016-10-03 19:44:57 +02001432 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001433 """
1434 Run the tests
1435
1436 :param test:
1437
1438 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001439 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001440
1441 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001442 if not self.print_summary:
1443 self.stream = self.orig_stream
1444 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001445 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001446
1447
1448class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001449 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001450 self.logger = logger
1451 self.args = args
1452 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001453 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001454 super(Worker, self).__init__()
1455
1456 def run(self):
1457 executable = self.args[0]
1458 self.logger.debug("Running executable w/args `%s'" % self.args)
1459 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001460 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001461 env["CK_LOG_FILE_NAME"] = "-"
1462 self.process = subprocess.Popen(
1463 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1464 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1465 out, err = self.process.communicate()
1466 self.logger.debug("Finished running `%s'" % executable)
1467 self.logger.info("Return code is `%s'" % self.process.returncode)
1468 self.logger.info(single_line_delim)
1469 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1470 self.logger.info(single_line_delim)
1471 self.logger.info(out)
1472 self.logger.info(single_line_delim)
1473 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1474 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001475 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001476 self.logger.info(single_line_delim)
1477 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001478
Klement Sekera6aa58b72019-05-16 14:34:55 +02001479
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001480if __name__ == '__main__':
1481 pass