blob: 79dfe1ae8664896f05e14d8a31f92e1e6621d340 [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
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040079 def __init__(self, rv=None, testcase=None, method_name=None):
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040080 self.rv = rv
81 self.signal_name = None
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040082 self.testcase = testcase
83 self.method_name = method_name
84
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040085 try:
86 self.signal_name = VppDiedError.signals_by_value[-rv]
87 except KeyError:
88 pass
89
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -040090 if testcase is None and method_name is None:
91 in_msg = ''
92 else:
93 in_msg = 'running %s.%s ' % (testcase, method_name)
94
95 msg = "VPP subprocess died %sunexpectedly with return code: %d%s." % (
96 in_msg,
Paul Vinciguerra496b0de2019-06-20 12:24:12 -040097 self.rv,
98 ' [%s]' % self.signal_name if
99 self.signal_name is not None else '')
100 super(VppDiedError, self).__init__(msg)
101
102
Damjan Marionf56b77a2016-10-03 19:44:57 +0200103class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200104 """Private class to create packet info object.
105
106 Help process information about the next packet.
107 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200108 """
Matej Klotton86d87c42016-11-11 11:38:55 +0100109 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200110 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100111 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200112 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100113 #: Store the index of the destination packet generator interface
114 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200115 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +0100116 #: Store expected ip version
117 ip = -1
118 #: Store expected upper protocol
119 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +0100120 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200121 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200122
Matej Klotton16a14cd2016-12-07 15:09:13 +0100123 def __eq__(self, other):
124 index = self.index == other.index
125 src = self.src == other.src
126 dst = self.dst == other.dst
127 data = self.data == other.data
128 return index and src and dst and data
129
Klement Sekeraf62ae122016-10-11 11:47:09 +0200130
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100131def pump_output(testclass):
132 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100133 stdout_fragment = ""
134 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400135 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100136 readable = select.select([testclass.vpp.stdout.fileno(),
137 testclass.vpp.stderr.fileno(),
138 testclass.pump_thread_wakeup_pipe[0]],
139 [], [])[0]
140 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100141 read = os.read(testclass.vpp.stdout.fileno(), 102400)
142 if len(read) > 0:
143 split = read.splitlines(True)
144 if len(stdout_fragment) > 0:
145 split[0] = "%s%s" % (stdout_fragment, split[0])
146 if len(split) > 0 and split[-1].endswith("\n"):
147 limit = None
148 else:
149 limit = -1
150 stdout_fragment = split[-1]
151 testclass.vpp_stdout_deque.extend(split[:limit])
152 if not testclass.cache_vpp_output:
153 for line in split[:limit]:
154 testclass.logger.debug(
155 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100156 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100157 read = os.read(testclass.vpp.stderr.fileno(), 102400)
158 if len(read) > 0:
159 split = read.splitlines(True)
160 if len(stderr_fragment) > 0:
161 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan7f991832018-12-06 17:35:12 +0100162 if len(split) > 0 and split[-1].endswith(b"\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100163 limit = None
164 else:
165 limit = -1
166 stderr_fragment = split[-1]
167 testclass.vpp_stderr_deque.extend(split[:limit])
168 if not testclass.cache_vpp_output:
169 for line in split[:limit]:
170 testclass.logger.debug(
171 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800172 # ignoring the dummy pipe here intentionally - the
173 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200174
175
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800176def _is_skip_aarch64_set():
juraj.linkes68ebc832018-11-29 09:37:08 +0100177 return os.getenv('SKIP_AARCH64', 'n').lower() in ('yes', 'y', '1')
178
Klement Sekera6aa58b72019-05-16 14:34:55 +0200179
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800180is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100181
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800182
183def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100184 return platform.machine() == 'aarch64'
185
Klement Sekera6aa58b72019-05-16 14:34:55 +0200186
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800187is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100188
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800189
190def _running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100191 s = os.getenv("EXTENDED_TESTS", "n")
192 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100193
Klement Sekera6aa58b72019-05-16 14:34:55 +0200194
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800195running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100196
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800197
198def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100199 os_id = os.getenv("OS_ID", "")
200 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200201
Klement Sekera6aa58b72019-05-16 14:34:55 +0200202
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800203running_on_centos = _running_on_centos
204
Klement Sekerad3e671e2017-09-29 12:36:37 +0200205
Klement Sekera909a6a12017-08-08 04:33:53 +0200206class KeepAliveReporter(object):
207 """
208 Singleton object which reports test start to parent process
209 """
210 _shared_state = {}
211
212 def __init__(self):
213 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800214 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200215
216 @property
217 def pipe(self):
218 return self._pipe
219
220 @pipe.setter
221 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800222 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200223 raise Exception("Internal error - pipe should only be set once.")
224 self._pipe = pipe
225
juraj.linkes40dd73b2018-09-21 13:55:16 +0200226 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200227 """
228 Write current test tmpdir & desc to keep-alive pipe to signal liveness
229 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200230 if self.pipe is None:
231 # if not running forked..
232 return
233
Klement Sekera909a6a12017-08-08 04:33:53 +0200234 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200235 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200236 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200237 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200238
Dave Wallacee2efd122017-09-30 22:04:21 -0400239 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200240
241
Damjan Marionf56b77a2016-10-03 19:44:57 +0200242class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100243 """This subclass is a base class for VPP test cases that are implemented as
244 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200245 """
246
Ole Troana45dc072018-12-21 16:04:22 +0100247 extra_vpp_punt_config = []
248 extra_vpp_plugin_config = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100249
Klement Sekeraf62ae122016-10-11 11:47:09 +0200250 @property
251 def packet_infos(self):
252 """List of packet infos"""
253 return self._packet_infos
254
Klement Sekeradab231a2016-12-21 08:50:14 +0100255 @classmethod
256 def get_packet_count_for_if_idx(cls, dst_if_index):
257 """Get the number of packet info for specified destination if index"""
258 if dst_if_index in cls._packet_count_for_dst_if_idx:
259 return cls._packet_count_for_dst_if_idx[dst_if_index]
260 else:
261 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200262
263 @classmethod
264 def instance(cls):
265 """Return the instance of this testcase"""
266 return cls.test_instance
267
Damjan Marionf56b77a2016-10-03 19:44:57 +0200268 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200269 def set_debug_flags(cls, d):
270 cls.debug_core = False
271 cls.debug_gdb = False
272 cls.debug_gdbserver = False
273 if d is None:
274 return
275 dl = d.lower()
276 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200277 cls.debug_core = True
278 elif dl == "gdb":
279 cls.debug_gdb = True
280 elif dl == "gdbserver":
281 cls.debug_gdbserver = True
282 else:
283 raise Exception("Unrecognized DEBUG option: '%s'" % d)
284
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800285 @staticmethod
286 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200287 cpu_usage_list = [set(range(psutil.cpu_count()))]
288 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
289 if 'vpp_main' == p.info['name']]
290 for vpp_process in vpp_processes:
291 for cpu_usage_set in cpu_usage_list:
292 try:
293 cpu_num = vpp_process.cpu_num()
294 if cpu_num in cpu_usage_set:
295 cpu_usage_set_index = cpu_usage_list.index(
296 cpu_usage_set)
297 if cpu_usage_set_index == len(cpu_usage_list) - 1:
298 cpu_usage_list.append({cpu_num})
299 else:
300 cpu_usage_list[cpu_usage_set_index + 1].add(
301 cpu_num)
302 cpu_usage_set.remove(cpu_num)
303 break
304 except psutil.NoSuchProcess:
305 pass
306
307 for cpu_usage_set in cpu_usage_list:
308 if len(cpu_usage_set) > 0:
309 min_usage_set = cpu_usage_set
310 break
311
312 return random.choice(tuple(min_usage_set))
313
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800314 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200315 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200316 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100317 s = os.getenv("STEP", "n")
318 cls.step = True if s.lower() in ("y", "yes", "1") else False
319 d = os.getenv("DEBUG", None)
320 c = os.getenv("CACHE_OUTPUT", "1")
321 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200322 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100323 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
324 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400325 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100326 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
327 plugin_path = None
328 if cls.plugin_path is not None:
329 if cls.extern_plugin_path is not None:
330 plugin_path = "%s:%s" % (
331 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100332 else:
333 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100334 elif cls.extern_plugin_path is not None:
335 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100336 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100337 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100338 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100339 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100340 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100341 if size is not None:
342 coredump_size = "coredump-size %s" % size
343 if coredump_size is None:
344 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200345
Ole Troana45dc072018-12-21 16:04:22 +0100346 cpu_core_number = cls.get_least_used_cpu()
juraj.linkes184870a2018-07-16 14:22:01 +0200347
Ole Troana45dc072018-12-21 16:04:22 +0100348 cls.vpp_cmdline = [cls.vpp_bin, "unix",
349 "{", "nodaemon", debug_cli, "full-coredump",
350 coredump_size, "runtime-dir", cls.tempdir, "}",
351 "api-trace", "{", "on", "}", "api-segment", "{",
352 "prefix", cls.shm_prefix, "}", "cpu", "{",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200353 "main-core", str(cpu_core_number), "}",
354 "statseg", "{", "socket-name", cls.stats_sock, "}",
355 "socksvr", "{", "socket-name", cls.api_sock, "}",
356 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100357 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200358 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100359 "}", "plugin", "unittest_plugin.so", "{", "enable",
360 "}"] + cls.extra_vpp_plugin_config + ["}", ]
361 if cls.extra_vpp_punt_config is not None:
362 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100363 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100364 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400365 if cls.test_plugin_path is not None:
366 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
367
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100368 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
369 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200370
371 @classmethod
372 def wait_for_enter(cls):
373 if cls.debug_gdbserver:
374 print(double_line_delim)
375 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
376 elif cls.debug_gdb:
377 print(double_line_delim)
378 print("Spawned VPP with PID: %d" % cls.vpp.pid)
379 else:
380 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
381 return
382 print(single_line_delim)
383 print("You can debug the VPP using e.g.:")
384 if cls.debug_gdbserver:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400385 print("sudo gdb " + cls.vpp_bin +
386 " -ex 'target remote localhost:7777'")
Klement Sekera277b89c2016-10-28 13:20:27 +0200387 print("Now is the time to attach a gdb by running the above "
388 "command, set up breakpoints etc. and then resume VPP from "
389 "within gdb by issuing the 'continue' command")
390 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400391 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Klement Sekera277b89c2016-10-28 13:20:27 +0200392 print("Now is the time to attach a gdb by running the above "
393 "command and set up breakpoints etc.")
394 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800395 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200396
397 @classmethod
398 def run_vpp(cls):
399 cmdline = cls.vpp_cmdline
400
401 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100402 gdbserver = '/usr/bin/gdbserver'
403 if not os.path.isfile(gdbserver) or \
404 not os.access(gdbserver, os.X_OK):
405 raise Exception("gdbserver binary '%s' does not exist or is "
406 "not executable" % gdbserver)
407
408 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200409 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
410
Klement Sekera931be3a2016-11-03 05:36:01 +0100411 try:
412 cls.vpp = subprocess.Popen(cmdline,
413 stdout=subprocess.PIPE,
414 stderr=subprocess.PIPE,
415 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800416 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800417 cls.logger.critical("Subprocess returned with non-0 return code: ("
418 "%s)", e.returncode)
419 raise
420 except OSError as e:
421 cls.logger.critical("Subprocess returned with OS error: "
422 "(%s) %s", e.errno, e.strerror)
423 raise
424 except Exception as e:
425 cls.logger.exception("Subprocess returned unexpected from "
426 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100427 raise
428
Klement Sekera277b89c2016-10-28 13:20:27 +0200429 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100430
Damjan Marionf56b77a2016-10-03 19:44:57 +0200431 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200432 def wait_for_stats_socket(cls):
433 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800434 ok = False
435 while time.time() < deadline or \
436 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200437 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800438 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200439 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700440 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800441 if not ok:
442 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200443
444 @classmethod
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400445 def wait_for_coredump(cls):
446 corefile = cls.tempdir + "/core"
447 if os.path.isfile(corefile):
448 cls.logger.error("Waiting for coredump to complete: %s", corefile)
449 curr_size = os.path.getsize(corefile)
450 deadline = time.time() + 60
451 ok = False
452 while time.time() < deadline:
453 cls.sleep(1)
454 size = curr_size
455 curr_size = os.path.getsize(corefile)
456 if size == curr_size:
457 ok = True
458 break
459 if not ok:
460 cls.logger.error("Timed out waiting for coredump to complete:"
461 " %s", corefile)
462 else:
463 cls.logger.error("Coredump complete: %s, size %d",
464 corefile, curr_size)
465
466 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200467 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200468 """
469 Perform class setup before running the testcase
470 Remove shared memory files, start vpp and connect the vpp-api
471 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800472 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100473 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100474 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100475 cls.logger = get_logger(cls.__name__)
476 if hasattr(cls, 'parallel_handler'):
477 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100478 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700479
Klement Sekeraf62ae122016-10-11 11:47:09 +0200480 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200481 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200482 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200483 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200484 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
485 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100486 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
487 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200488 cls.file_handler.setLevel(DEBUG)
489 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700490 cls.logger.debug("--- setUpClass() for %s called ---" %
491 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200492 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200493 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200494 cls.logger.info("Temporary dir is %s, shm prefix is %s",
495 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200496 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100497 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100498 cls._captures = []
499 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200500 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100501 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100502 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200503 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200504 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200505 # need to catch exceptions here because if we raise, then the cleanup
506 # doesn't get called and we might end with a zombie vpp
507 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200508 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200509 cls.reporter.send_keep_alive(cls, 'setUpClass')
510 VppTestResult.current_test_case_info = TestCaseInfo(
511 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100512 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100513 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100514 cls.pump_thread_stop_flag = Event()
515 cls.pump_thread_wakeup_pipe = os.pipe()
516 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100517 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100518 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200519 if cls.debug_gdb or cls.debug_gdbserver:
520 read_timeout = 0
521 else:
522 read_timeout = 5
523 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
524 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100525 if cls.step:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400526 hook = hookmodule.StepHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100527 else:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400528 hook = hookmodule.PollHook(cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100529 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200530 cls.wait_for_stats_socket()
531 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200532 try:
533 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100534 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200535 cls.vpp_startup_failed = True
536 cls.logger.critical(
537 "VPP died shortly after startup, check the"
538 " output to standard error for possible cause")
539 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100540 try:
541 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100542 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100543 try:
544 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100545 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100546 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100547 if cls.debug_gdbserver:
548 print(colorize("You're running VPP inside gdbserver but "
549 "VPP-API connection failed, did you forget "
550 "to 'continue' VPP from within gdb?", RED))
551 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100552 except Exception:
Paul Vinciguerra496b0de2019-06-20 12:24:12 -0400553
554 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100555 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200556
Damjan Marionf56b77a2016-10-03 19:44:57 +0200557 @classmethod
558 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200559 """
560 Disconnect vpp-api, kill vpp and cleanup shared memory files
561 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200562 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
563 cls.vpp.poll()
564 if cls.vpp.returncode is None:
565 print(double_line_delim)
566 print("VPP or GDB server is still running")
567 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800568 input("When done debugging, press ENTER to kill the "
569 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200570
juraj.linkes184870a2018-07-16 14:22:01 +0200571 # first signal that we want to stop the pump thread, then wake it up
572 if hasattr(cls, 'pump_thread_stop_flag'):
573 cls.pump_thread_stop_flag.set()
574 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100575 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100576 if hasattr(cls, 'pump_thread'):
577 cls.logger.debug("Waiting for pump thread to stop")
578 cls.pump_thread.join()
579 if hasattr(cls, 'vpp_stderr_reader_thread'):
580 cls.logger.debug("Waiting for stdderr pump to stop")
581 cls.vpp_stderr_reader_thread.join()
582
Klement Sekeraf62ae122016-10-11 11:47:09 +0200583 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100584 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700585 cls.logger.debug("Disconnecting class vapi client on %s",
586 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100587 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700588 cls.logger.debug("Deleting class vapi attribute on %s",
589 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100590 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200591 cls.vpp.poll()
592 if cls.vpp.returncode is None:
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400593 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100594 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400595 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100596 cls.logger.debug("Waiting for vpp to die")
597 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700598 cls.logger.debug("Deleting class vpp attribute on %s",
599 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200600 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200601
Klement Sekera3747c752017-04-10 06:30:17 +0200602 if cls.vpp_startup_failed:
603 stdout_log = cls.logger.info
604 stderr_log = cls.logger.critical
605 else:
606 stdout_log = cls.logger.info
607 stderr_log = cls.logger.info
608
Klement Sekerae4504c62016-12-08 10:16:41 +0100609 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200610 stdout_log(single_line_delim)
611 stdout_log('VPP output to stdout while running %s:', cls.__name__)
612 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100613 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200614 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
615 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200616 stdout_log('\n%s', vpp_output)
617 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200618
Klement Sekerae4504c62016-12-08 10:16:41 +0100619 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200620 stderr_log(single_line_delim)
621 stderr_log('VPP output to stderr while running %s:', cls.__name__)
622 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100623 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200624 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
625 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200626 stderr_log('\n%s', vpp_output)
627 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200628
Damjan Marionf56b77a2016-10-03 19:44:57 +0200629 @classmethod
630 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200631 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700632 cls.logger.debug("--- tearDownClass() for %s called ---" %
633 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200634 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200635 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200636 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100637 cls.reset_packet_infos()
638 if debug_framework:
639 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200640
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700641 def show_commands_at_teardown(self):
642 """ Allow subclass specific teardown logging additions."""
643 self.logger.info("--- No test specific show commands provided. ---")
644
Damjan Marionf56b77a2016-10-03 19:44:57 +0200645 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200646 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100647 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
648 (self.__class__.__name__, self._testMethodName,
649 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700650
651 try:
652 if not self.vpp_dead:
653 self.logger.debug(self.vapi.cli("show trace max 1000"))
654 self.logger.info(self.vapi.ppcli("show interface"))
655 self.logger.info(self.vapi.ppcli("show hardware"))
656 self.logger.info(self.statistics.set_errors_str())
657 self.logger.info(self.vapi.ppcli("show run"))
658 self.logger.info(self.vapi.ppcli("show log"))
659 self.logger.info("Logging testcase specific show commands.")
660 self.show_commands_at_teardown()
661 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500662 # Save/Dump VPP api trace log
663 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
664 tmp_api_trace = "/tmp/%s" % api_trace
665 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
666 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
667 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
668 vpp_api_trace_log))
669 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500670 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500671 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700672 except VppTransportShmemIOError:
673 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
674 "Cannot log show commands.")
675 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100676 else:
677 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200678
Damjan Marionf56b77a2016-10-03 19:44:57 +0200679 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200680 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800681 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200682 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100683 if self.vpp_dead:
Paul Vinciguerra5dd6a7b2019-06-19 10:29:24 -0400684 raise VppDiedError(self.__class__.__name__, self._testMethodName,
685 "VPP is dead when setting up the test "
686 "(%s.%s)." % (self.__class__.__name__,
687 self._testMethodName))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100688 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100689 self.vpp_stdout_deque.append(
690 "--- test setUp() for %s.%s(%s) starts here ---\n" %
691 (self.__class__.__name__, self._testMethodName,
692 self._testMethodDoc))
693 self.vpp_stderr_deque.append(
694 "--- test setUp() for %s.%s(%s) starts here ---\n" %
695 (self.__class__.__name__, self._testMethodName,
696 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200697 self.vapi.cli("clear trace")
698 # store the test instance inside the test class - so that objects
699 # holding the class can access instance methods (like assertEqual)
700 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200701
Damjan Marionf56b77a2016-10-03 19:44:57 +0200702 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200703 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200704 """
705 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200706
Klement Sekera75e7d132017-09-20 08:26:30 +0200707 :param interfaces: iterable interface indexes (if None,
708 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200709
Klement Sekeraf62ae122016-10-11 11:47:09 +0200710 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200711 if interfaces is None:
712 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200713 for i in interfaces:
714 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200715
Damjan Marionf56b77a2016-10-03 19:44:57 +0200716 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100717 def register_capture(cls, cap_name):
718 """ Register a capture in the testclass """
719 # add to the list of captures with current timestamp
720 cls._captures.append((time.time(), cap_name))
721 # filter out from zombies
722 cls._zombie_captures = [(stamp, name)
723 for (stamp, name) in cls._zombie_captures
724 if name != cap_name]
725
726 @classmethod
727 def pg_start(cls):
728 """ Remove any zombie captures and enable the packet generator """
729 # how long before capture is allowed to be deleted - otherwise vpp
730 # crashes - 100ms seems enough (this shouldn't be needed at all)
731 capture_ttl = 0.1
732 now = time.time()
733 for stamp, cap_name in cls._zombie_captures:
734 wait = stamp + capture_ttl - now
735 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100736 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100737 now = time.time()
738 cls.logger.debug("Removing zombie capture %s" % cap_name)
739 cls.vapi.cli('packet-generator delete %s' % cap_name)
740
Klement Sekerad91fa612019-01-15 13:25:09 +0100741 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200742 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100743 cls._zombie_captures = cls._captures
744 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200745
Damjan Marionf56b77a2016-10-03 19:44:57 +0200746 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200747 def create_pg_interfaces(cls, interfaces):
748 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100749 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200750
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100751 :param interfaces: iterable indexes of the interfaces.
752 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200753
Klement Sekeraf62ae122016-10-11 11:47:09 +0200754 """
755 result = []
756 for i in interfaces:
757 intf = VppPGInterface(cls, i)
758 setattr(cls, intf.name, intf)
759 result.append(intf)
760 cls.pg_interfaces = result
761 return result
762
Matej Klotton0178d522016-11-04 11:11:44 +0100763 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200764 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100765 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100766 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100767
Klement Sekerab9ef2732018-06-24 22:49:33 +0200768 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100769 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100770 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200771 result = [VppLoInterface(cls) for i in range(count)]
772 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100773 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100774 cls.lo_interfaces = result
775 return result
776
Neale Ranns192b13f2019-03-15 02:16:20 -0700777 @classmethod
778 def create_bvi_interfaces(cls, count):
779 """
780 Create BVI interfaces.
781
782 :param count: number of interfaces created.
783 :returns: List of created interfaces.
784 """
785 result = [VppBviInterface(cls) for i in range(count)]
786 for intf in result:
787 setattr(cls, intf.name, intf)
788 cls.bvi_interfaces = result
789 return result
790
Damjan Marionf56b77a2016-10-03 19:44:57 +0200791 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200792 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200793 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200794 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200795 NOTE: Currently works only when Raw layer is present.
796
797 :param packet: packet
798 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200799 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200800
801 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200802 packet_len = len(packet) + 4
803 extend = size - packet_len
804 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200805 num = (extend // len(padding)) + 1
806 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200807
Klement Sekeradab231a2016-12-21 08:50:14 +0100808 @classmethod
809 def reset_packet_infos(cls):
810 """ Reset the list of packet info objects and packet counts to zero """
811 cls._packet_infos = {}
812 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200813
Klement Sekeradab231a2016-12-21 08:50:14 +0100814 @classmethod
815 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200816 """
817 Create packet info object containing the source and destination indexes
818 and add it to the testcase's packet info list
819
Klement Sekeradab231a2016-12-21 08:50:14 +0100820 :param VppInterface src_if: source interface
821 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200822
823 :returns: _PacketInfo object
824
825 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200826 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100827 info.index = len(cls._packet_infos)
828 info.src = src_if.sw_if_index
829 info.dst = dst_if.sw_if_index
830 if isinstance(dst_if, VppSubInterface):
831 dst_idx = dst_if.parent.sw_if_index
832 else:
833 dst_idx = dst_if.sw_if_index
834 if dst_idx in cls._packet_count_for_dst_if_idx:
835 cls._packet_count_for_dst_if_idx[dst_idx] += 1
836 else:
837 cls._packet_count_for_dst_if_idx[dst_idx] = 1
838 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200839 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200840
Damjan Marionf56b77a2016-10-03 19:44:57 +0200841 @staticmethod
842 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200843 """
844 Convert _PacketInfo object to packet payload
845
846 :param info: _PacketInfo object
847
848 :returns: string containing serialized data from packet info
849 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100850 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
851 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200852
Damjan Marionf56b77a2016-10-03 19:44:57 +0200853 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800854 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200855 """
856 Convert packet payload to _PacketInfo object
857
858 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700859 :type payload: <class 'scapy.packet.Raw'>
860 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800861 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700862 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200863 :returns: _PacketInfo object containing de-serialized data from payload
864
865 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800866 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200867 info = _PacketInfo()
868 info.index = int(numbers[0])
869 info.src = int(numbers[1])
870 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100871 info.ip = int(numbers[3])
872 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200873 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200874
Damjan Marionf56b77a2016-10-03 19:44:57 +0200875 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200876 """
877 Iterate over the packet info list stored in the testcase
878 Start iteration with first element if info is None
879 Continue based on index in info if info is specified
880
881 :param info: info or None
882 :returns: next info in list or None if no more infos
883 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200884 if info is None:
885 next_index = 0
886 else:
887 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100888 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200889 return None
890 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100891 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200892
Klement Sekeraf62ae122016-10-11 11:47:09 +0200893 def get_next_packet_info_for_interface(self, src_index, info):
894 """
895 Search the packet info list for the next packet info with same source
896 interface index
897
898 :param src_index: source interface index to search for
899 :param info: packet info - where to start the search
900 :returns: packet info or None
901
902 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200903 while True:
904 info = self.get_next_packet_info(info)
905 if info is None:
906 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200907 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200908 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200909
Klement Sekeraf62ae122016-10-11 11:47:09 +0200910 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
911 """
912 Search the packet info list for the next packet info with same source
913 and destination interface indexes
914
915 :param src_index: source interface index to search for
916 :param dst_index: destination interface index to search for
917 :param info: packet info - where to start the search
918 :returns: packet info or None
919
920 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200921 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200922 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200923 if info is None:
924 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200925 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200926 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200927
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200928 def assert_equal(self, real_value, expected_value, name_or_class=None):
929 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100930 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200931 return
932 try:
933 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
934 msg = msg % (getdoc(name_or_class).strip(),
935 real_value, str(name_or_class(real_value)),
936 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100937 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200938 msg = "Invalid %s: %s does not match expected value %s" % (
939 name_or_class, real_value, expected_value)
940
941 self.assertEqual(real_value, expected_value, msg)
942
Klement Sekerab17dd962017-01-09 07:43:48 +0100943 def assert_in_range(self,
944 real_value,
945 expected_min,
946 expected_max,
947 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200948 if name is None:
949 msg = None
950 else:
951 msg = "Invalid %s: %s out of range <%s,%s>" % (
952 name, real_value, expected_min, expected_max)
953 self.assertTrue(expected_min <= real_value <= expected_max, msg)
954
Klement Sekerad81ae412018-05-16 10:52:54 +0200955 def assert_packet_checksums_valid(self, packet,
956 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700957 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200958 self.logger.debug(
959 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200960 udp_layers = ['UDP', 'UDPerror']
961 checksum_fields = ['cksum', 'chksum']
962 checksums = []
963 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700964 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200965 while True:
966 layer = temp.getlayer(counter)
967 if layer:
968 for cf in checksum_fields:
969 if hasattr(layer, cf):
970 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +0200971 0 == getattr(layer, cf) and \
972 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200973 continue
974 delattr(layer, cf)
975 checksums.append((counter, cf))
976 else:
977 break
978 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200979 if 0 == len(checksums):
980 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700981 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200982 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200983 calc_sum = getattr(temp[layer], cf)
984 self.assert_equal(
985 getattr(received[layer], cf), calc_sum,
986 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
987 self.logger.debug(
988 "Checksum field `%s` on `%s` layer has correct value `%s`" %
989 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200990
991 def assert_checksum_valid(self, received_packet, layer,
992 field_name='chksum',
993 ignore_zero_checksum=False):
994 """ Check checksum of received packet on given layer """
995 received_packet_checksum = getattr(received_packet[layer], field_name)
996 if ignore_zero_checksum and 0 == received_packet_checksum:
997 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700998 recalculated = received_packet.__class__(
999 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +02001000 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -07001001 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +02001002 self.assert_equal(received_packet_checksum,
1003 getattr(recalculated[layer], field_name),
1004 "packet checksum on layer: %s" % layer)
1005
1006 def assert_ip_checksum_valid(self, received_packet,
1007 ignore_zero_checksum=False):
1008 self.assert_checksum_valid(received_packet, 'IP',
1009 ignore_zero_checksum=ignore_zero_checksum)
1010
1011 def assert_tcp_checksum_valid(self, received_packet,
1012 ignore_zero_checksum=False):
1013 self.assert_checksum_valid(received_packet, 'TCP',
1014 ignore_zero_checksum=ignore_zero_checksum)
1015
1016 def assert_udp_checksum_valid(self, received_packet,
1017 ignore_zero_checksum=True):
1018 self.assert_checksum_valid(received_packet, 'UDP',
1019 ignore_zero_checksum=ignore_zero_checksum)
1020
1021 def assert_embedded_icmp_checksum_valid(self, received_packet):
1022 if received_packet.haslayer(IPerror):
1023 self.assert_checksum_valid(received_packet, 'IPerror')
1024 if received_packet.haslayer(TCPerror):
1025 self.assert_checksum_valid(received_packet, 'TCPerror')
1026 if received_packet.haslayer(UDPerror):
1027 self.assert_checksum_valid(received_packet, 'UDPerror',
1028 ignore_zero_checksum=True)
1029 if received_packet.haslayer(ICMPerror):
1030 self.assert_checksum_valid(received_packet, 'ICMPerror')
1031
1032 def assert_icmp_checksum_valid(self, received_packet):
1033 self.assert_checksum_valid(received_packet, 'ICMP')
1034 self.assert_embedded_icmp_checksum_valid(received_packet)
1035
1036 def assert_icmpv6_checksum_valid(self, pkt):
1037 if pkt.haslayer(ICMPv6DestUnreach):
1038 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1039 self.assert_embedded_icmp_checksum_valid(pkt)
1040 if pkt.haslayer(ICMPv6EchoRequest):
1041 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1042 if pkt.haslayer(ICMPv6EchoReply):
1043 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1044
Klement Sekera3a343d42019-05-16 14:35:46 +02001045 def get_packet_counter(self, counter):
1046 if counter.startswith("/"):
1047 counter_value = self.statistics.get_counter(counter)
1048 else:
1049 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001050 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001051 for i in range(1, len(counters) - 1):
1052 results = counters[i].split()
1053 if results[1] == counter:
1054 counter_value = int(results[0])
1055 break
1056 return counter_value
1057
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001058 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001059 counter_value = self.get_packet_counter(counter)
1060 self.assert_equal(counter_value, expected_value,
1061 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001062
Ole Troan233e4682019-05-16 15:01:34 +02001063 def assert_error_counter_equal(self, counter, expected_value):
1064 counter_value = self.statistics.get_err_counter(counter)
1065 self.assert_equal(counter_value, expected_value,
1066 "error counter `%s'" % counter)
1067
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001068 @classmethod
1069 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001070
1071 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1072 # * by Guido, only the main thread can be interrupted.
1073 # */
1074 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1075 if timeout == 0:
1076 # yield quantum
1077 if hasattr(os, 'sched_yield'):
1078 os.sched_yield()
1079 else:
1080 time.sleep(0)
1081 return
1082
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001083 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001084 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001085 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001086 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001087 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001088 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001089 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001090 "slept for %es instead of ~%es!",
1091 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001092 if hasattr(cls, 'logger'):
1093 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001094 "Finished sleep (%s) - slept %es (wanted %es)",
1095 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001096
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001097 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001098 self.vapi.cli("clear trace")
1099 intf.add_stream(pkts)
1100 self.pg_enable_capture(self.pg_interfaces)
1101 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001102
1103 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1104 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001105 if not timeout:
1106 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001107 for i in self.pg_interfaces:
1108 i.get_capture(0, timeout=timeout)
1109 i.assert_nothing_captured(remark=remark)
1110 timeout = 0.1
1111
Neale Rannsd7603d92019-03-28 08:56:10 +00001112 def send_and_expect(self, intf, pkts, output, n_rx=None):
1113 if not n_rx:
1114 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001115 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001116 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001117 return rx
1118
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001119 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1120 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001121 rx = output.get_capture(len(pkts))
1122 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001123 if not timeout:
1124 timeout = 1
1125 for i in self.pg_interfaces:
1126 if i not in outputs:
1127 i.get_capture(0, timeout=timeout)
1128 i.assert_nothing_captured()
1129 timeout = 0.1
1130
Neale Ranns52fae862018-01-08 04:41:42 -08001131 return rx
1132
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001133 def runTest(self):
1134 """ unittest calls runTest when TestCase is instantiated without a
1135 test case. Use case: Writing unittests against VppTestCase"""
1136 pass
1137
Damjan Marionf56b77a2016-10-03 19:44:57 +02001138
juraj.linkes184870a2018-07-16 14:22:01 +02001139def get_testcase_doc_name(test):
1140 return getdoc(test.__class__).splitlines()[0]
1141
1142
Ole Trøan5ba91592018-11-22 10:01:09 +00001143def get_test_description(descriptions, test):
1144 short_description = test.shortDescription()
1145 if descriptions and short_description:
1146 return short_description
1147 else:
1148 return str(test)
1149
1150
juraj.linkes40dd73b2018-09-21 13:55:16 +02001151class TestCaseInfo(object):
1152 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1153 self.logger = logger
1154 self.tempdir = tempdir
1155 self.vpp_pid = vpp_pid
1156 self.vpp_bin_path = vpp_bin_path
1157 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001158
1159
Damjan Marionf56b77a2016-10-03 19:44:57 +02001160class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001161 """
1162 @property result_string
1163 String variable to store the test case result string.
1164 @property errors
1165 List variable containing 2-tuples of TestCase instances and strings
1166 holding formatted tracebacks. Each tuple represents a test which
1167 raised an unexpected exception.
1168 @property failures
1169 List variable containing 2-tuples of TestCase instances and strings
1170 holding formatted tracebacks. Each tuple represents a test where
1171 a failure was explicitly signalled using the TestCase.assert*()
1172 methods.
1173 """
1174
juraj.linkes40dd73b2018-09-21 13:55:16 +02001175 failed_test_cases_info = set()
1176 core_crash_test_cases_info = set()
1177 current_test_case_info = None
1178
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001179 def __init__(self, stream=None, descriptions=None, verbosity=None,
1180 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001181 """
Klement Sekerada505f62017-01-04 12:58:53 +01001182 :param stream File descriptor to store where to report test results.
1183 Set to the standard error stream by default.
1184 :param descriptions Boolean variable to store information if to use
1185 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001186 :param verbosity Integer variable to store required verbosity level.
1187 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001188 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001189 self.stream = stream
1190 self.descriptions = descriptions
1191 self.verbosity = verbosity
1192 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001193 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001194
Damjan Marionf56b77a2016-10-03 19:44:57 +02001195 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001196 """
1197 Record a test succeeded result
1198
1199 :param test:
1200
1201 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001202 if self.current_test_case_info:
1203 self.current_test_case_info.logger.debug(
1204 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1205 test._testMethodName,
1206 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001207 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001208 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001209
juraj.linkescae64f82018-09-19 15:01:47 +02001210 self.send_result_through_pipe(test, PASS)
1211
Klement Sekeraf62ae122016-10-11 11:47:09 +02001212 def addSkip(self, test, reason):
1213 """
1214 Record a test skipped.
1215
1216 :param test:
1217 :param reason:
1218
1219 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001220 if self.current_test_case_info:
1221 self.current_test_case_info.logger.debug(
1222 "--- addSkip() %s.%s(%s) called, reason is %s" %
1223 (test.__class__.__name__, test._testMethodName,
1224 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001225 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001226 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001227
juraj.linkescae64f82018-09-19 15:01:47 +02001228 self.send_result_through_pipe(test, SKIP)
1229
juraj.linkes40dd73b2018-09-21 13:55:16 +02001230 def symlink_failed(self):
1231 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001232 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001233 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001234 link_path = os.path.join(
1235 failed_dir,
1236 '%s-FAILED' %
1237 os.path.basename(self.current_test_case_info.tempdir))
1238 if self.current_test_case_info.logger:
1239 self.current_test_case_info.logger.debug(
1240 "creating a link to the failed test")
1241 self.current_test_case_info.logger.debug(
1242 "os.symlink(%s, %s)" %
1243 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001244 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001245 if self.current_test_case_info.logger:
1246 self.current_test_case_info.logger.debug(
1247 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001248 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001249 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001250
Klement Sekeraf413bef2017-08-15 07:09:02 +02001251 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001252 if self.current_test_case_info.logger:
1253 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001254
juraj.linkescae64f82018-09-19 15:01:47 +02001255 def send_result_through_pipe(self, test, result):
1256 if hasattr(self, 'test_framework_result_pipe'):
1257 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001258 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001259 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001260
juraj.linkes40dd73b2018-09-21 13:55:16 +02001261 def log_error(self, test, err, fn_name):
1262 if self.current_test_case_info:
1263 if isinstance(test, unittest.suite._ErrorHolder):
1264 test_name = test.description
1265 else:
1266 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1267 test._testMethodName,
1268 test._testMethodDoc)
1269 self.current_test_case_info.logger.debug(
1270 "--- %s() %s called, err is %s" %
1271 (fn_name, test_name, err))
1272 self.current_test_case_info.logger.debug(
1273 "formatted exception is:\n%s" %
1274 "".join(format_exception(*err)))
1275
1276 def add_error(self, test, err, unittest_fn, error_type):
1277 if error_type == FAIL:
1278 self.log_error(test, err, 'addFailure')
1279 error_type_str = colorize("FAIL", RED)
1280 elif error_type == ERROR:
1281 self.log_error(test, err, 'addError')
1282 error_type_str = colorize("ERROR", RED)
1283 else:
1284 raise Exception('Error type %s cannot be used to record an '
1285 'error or a failure' % error_type)
1286
1287 unittest_fn(self, test, err)
1288 if self.current_test_case_info:
1289 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1290 (error_type_str,
1291 self.current_test_case_info.tempdir)
1292 self.symlink_failed()
1293 self.failed_test_cases_info.add(self.current_test_case_info)
1294 if is_core_present(self.current_test_case_info.tempdir):
1295 if not self.current_test_case_info.core_crash_test:
1296 if isinstance(test, unittest.suite._ErrorHolder):
1297 test_name = str(test)
1298 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001299 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001300 get_testcase_doc_name(test), test.id())
1301 self.current_test_case_info.core_crash_test = test_name
1302 self.core_crash_test_cases_info.add(
1303 self.current_test_case_info)
1304 else:
1305 self.result_string = '%s [no temp dir]' % error_type_str
1306
1307 self.send_result_through_pipe(test, error_type)
1308
Damjan Marionf56b77a2016-10-03 19:44:57 +02001309 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001310 """
1311 Record a test failed result
1312
1313 :param test:
1314 :param err: error message
1315
1316 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001317 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001318
Damjan Marionf56b77a2016-10-03 19:44:57 +02001319 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001320 """
1321 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001322
Klement Sekeraf62ae122016-10-11 11:47:09 +02001323 :param test:
1324 :param err: error message
1325
1326 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001327 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001328
Damjan Marionf56b77a2016-10-03 19:44:57 +02001329 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001330 """
1331 Get test description
1332
1333 :param test:
1334 :returns: test description
1335
1336 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001337 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001338
Damjan Marionf56b77a2016-10-03 19:44:57 +02001339 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001340 """
1341 Start a test
1342
1343 :param test:
1344
1345 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001346
1347 def print_header(test):
1348 if not hasattr(test.__class__, '_header_printed'):
1349 print(double_line_delim)
1350 print(colorize(getdoc(test).splitlines()[0], GREEN))
1351 print(double_line_delim)
1352 test.__class__._header_printed = True
1353
1354 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001355
Damjan Marionf56b77a2016-10-03 19:44:57 +02001356 unittest.TestResult.startTest(self, test)
1357 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001358 self.stream.writeln(
1359 "Starting " + self.getDescription(test) + " ...")
1360 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001361
Damjan Marionf56b77a2016-10-03 19:44:57 +02001362 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001363 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001364 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001365
1366 :param test:
1367
1368 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001369 unittest.TestResult.stopTest(self, test)
1370 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001371 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001372 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001373 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001374 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001375 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001376 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001377 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001378
1379 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001380
Damjan Marionf56b77a2016-10-03 19:44:57 +02001381 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001382 """
1383 Print errors from running the test case
1384 """
juraj.linkesabec0122018-11-16 17:28:56 +01001385 if len(self.errors) > 0 or len(self.failures) > 0:
1386 self.stream.writeln()
1387 self.printErrorList('ERROR', self.errors)
1388 self.printErrorList('FAIL', self.failures)
1389
1390 # ^^ that is the last output from unittest before summary
1391 if not self.runner.print_summary:
1392 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1393 self.stream = devnull
1394 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001395
Damjan Marionf56b77a2016-10-03 19:44:57 +02001396 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001397 """
1398 Print error list to the output stream together with error type
1399 and test case description.
1400
1401 :param flavour: error type
1402 :param errors: iterable errors
1403
1404 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001405 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001406 self.stream.writeln(double_line_delim)
1407 self.stream.writeln("%s: %s" %
1408 (flavour, self.getDescription(test)))
1409 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001410 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001411
1412
Damjan Marionf56b77a2016-10-03 19:44:57 +02001413class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001414 """
Klement Sekera104543f2017-02-03 07:29:43 +01001415 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001416 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001417
Klement Sekeraf62ae122016-10-11 11:47:09 +02001418 @property
1419 def resultclass(self):
1420 """Class maintaining the results of the tests"""
1421 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001422
juraj.linkes184870a2018-07-16 14:22:01 +02001423 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001424 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001425 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001426 # ignore stream setting here, use hard-coded stdout to be in sync
1427 # with prints from VppTestCase methods ...
1428 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1429 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001430 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001431 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001432
juraj.linkesabec0122018-11-16 17:28:56 +01001433 self.orig_stream = self.stream
1434 self.resultclass.test_framework_result_pipe = result_pipe
1435
1436 self.print_summary = print_summary
1437
1438 def _makeResult(self):
1439 return self.resultclass(self.stream,
1440 self.descriptions,
1441 self.verbosity,
1442 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001443
Damjan Marionf56b77a2016-10-03 19:44:57 +02001444 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001445 """
1446 Run the tests
1447
1448 :param test:
1449
1450 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001451 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001452
1453 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001454 if not self.print_summary:
1455 self.stream = self.orig_stream
1456 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001457 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001458
1459
1460class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001461 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001462 self.logger = logger
1463 self.args = args
1464 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001465 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001466 super(Worker, self).__init__()
1467
1468 def run(self):
1469 executable = self.args[0]
1470 self.logger.debug("Running executable w/args `%s'" % self.args)
1471 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001472 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001473 env["CK_LOG_FILE_NAME"] = "-"
1474 self.process = subprocess.Popen(
1475 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1476 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1477 out, err = self.process.communicate()
1478 self.logger.debug("Finished running `%s'" % executable)
1479 self.logger.info("Return code is `%s'" % self.process.returncode)
1480 self.logger.info(single_line_delim)
1481 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1482 self.logger.info(single_line_delim)
1483 self.logger.info(out)
1484 self.logger.info(single_line_delim)
1485 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1486 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001487 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001488 self.logger.info(single_line_delim)
1489 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001490
Klement Sekera6aa58b72019-05-16 14:34:55 +02001491
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001492if __name__ == '__main__':
1493 pass