blob: fd91d4c9d5e790f4f0998afa6c960e3f0f3dbdb7 [file] [log] [blame]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001#!/usr/bin/env python
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
4import gc
Paul Vinciguerra72f00042018-11-25 11:05:13 -08005import sys
Ole Trøan162989e2018-11-26 10:27:50 +00006import os
7import select
8import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +02009import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020010import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080011import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000012import random
13import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080014import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010015import platform
Ole Trøan162989e2018-11-26 10:27:50 +000016from collections import deque
17from threading import Thread, Event
18from inspect import getdoc, isclass
19from traceback import format_exception
20from logging import FileHandler, DEBUG, Formatter
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070021
22import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000023from scapy.packet import Raw
24from hook import StepHook, PollHook, VppDiedError
Paul Vinciguerra919efad2018-12-17 21:43:43 -080025from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010026from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000027from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070028from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000029from vpp_papi_provider import VppPapiProvider
30from vpp_papi.vpp_stats import VPPStats
Paul Vinciguerra499ea642019-03-15 09:39:19 -070031from vpp_papi.vpp_transport_shmem import VppTransportShmemIOError
Ole Trøan162989e2018-11-26 10:27:50 +000032from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
33 get_logger, colorize
34from vpp_object import VppObjectRegistry
35from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020036from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
37from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
38from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080039
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010040if os.name == 'posix' and sys.version_info[0] < 3:
41 # using subprocess32 is recommended by python official documentation
42 # @ https://docs.python.org/2/library/subprocess.html
43 import subprocess32 as subprocess
44else:
45 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020046
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080047# Python2/3 compatible
48try:
49 input = raw_input
50except NameError:
51 pass
52
juraj.linkescae64f82018-09-19 15:01:47 +020053PASS = 0
54FAIL = 1
55ERROR = 2
56SKIP = 3
57TEST_RUN = 4
58
Klement Sekeraebbaf552018-02-17 13:41:33 +010059debug_framework = False
60if os.getenv('TEST_DEBUG', "0") == "1":
61 debug_framework = True
62 import debug_internal
63
Klement Sekeraf62ae122016-10-11 11:47:09 +020064"""
65 Test framework module.
66
67 The module provides a set of tools for constructing and running tests and
68 representing the results.
69"""
70
Klement Sekeraf62ae122016-10-11 11:47:09 +020071
Damjan Marionf56b77a2016-10-03 19:44:57 +020072class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020073 """Private class to create packet info object.
74
75 Help process information about the next packet.
76 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020077 """
Matej Klotton86d87c42016-11-11 11:38:55 +010078 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020079 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010080 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020081 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010082 #: Store the index of the destination packet generator interface
83 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020084 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010085 #: Store expected ip version
86 ip = -1
87 #: Store expected upper protocol
88 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010089 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020090 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020091
Matej Klotton16a14cd2016-12-07 15:09:13 +010092 def __eq__(self, other):
93 index = self.index == other.index
94 src = self.src == other.src
95 dst = self.dst == other.dst
96 data = self.data == other.data
97 return index and src and dst and data
98
Klement Sekeraf62ae122016-10-11 11:47:09 +020099
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100100def pump_output(testclass):
101 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100102 stdout_fragment = ""
103 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400104 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100105 readable = select.select([testclass.vpp.stdout.fileno(),
106 testclass.vpp.stderr.fileno(),
107 testclass.pump_thread_wakeup_pipe[0]],
108 [], [])[0]
109 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100110 read = os.read(testclass.vpp.stdout.fileno(), 102400)
111 if len(read) > 0:
112 split = read.splitlines(True)
113 if len(stdout_fragment) > 0:
114 split[0] = "%s%s" % (stdout_fragment, split[0])
115 if len(split) > 0 and split[-1].endswith("\n"):
116 limit = None
117 else:
118 limit = -1
119 stdout_fragment = split[-1]
120 testclass.vpp_stdout_deque.extend(split[:limit])
121 if not testclass.cache_vpp_output:
122 for line in split[:limit]:
123 testclass.logger.debug(
124 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100125 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100126 read = os.read(testclass.vpp.stderr.fileno(), 102400)
127 if len(read) > 0:
128 split = read.splitlines(True)
129 if len(stderr_fragment) > 0:
130 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan7f991832018-12-06 17:35:12 +0100131 if len(split) > 0 and split[-1].endswith(b"\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100132 limit = None
133 else:
134 limit = -1
135 stderr_fragment = split[-1]
136 testclass.vpp_stderr_deque.extend(split[:limit])
137 if not testclass.cache_vpp_output:
138 for line in split[:limit]:
139 testclass.logger.debug(
140 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800141 # ignoring the dummy pipe here intentionally - the
142 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200143
144
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800145def _is_skip_aarch64_set():
juraj.linkes68ebc832018-11-29 09:37:08 +0100146 return os.getenv('SKIP_AARCH64', 'n').lower() in ('yes', 'y', '1')
147
Klement Sekera6aa58b72019-05-16 14:34:55 +0200148
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800149is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100150
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800151
152def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100153 return platform.machine() == 'aarch64'
154
Klement Sekera6aa58b72019-05-16 14:34:55 +0200155
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800156is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100157
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800158
159def _running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100160 s = os.getenv("EXTENDED_TESTS", "n")
161 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100162
Klement Sekera6aa58b72019-05-16 14:34:55 +0200163
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800164running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100165
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800166
167def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100168 os_id = os.getenv("OS_ID", "")
169 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200170
Klement Sekera6aa58b72019-05-16 14:34:55 +0200171
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800172running_on_centos = _running_on_centos
173
Klement Sekerad3e671e2017-09-29 12:36:37 +0200174
Klement Sekera909a6a12017-08-08 04:33:53 +0200175class KeepAliveReporter(object):
176 """
177 Singleton object which reports test start to parent process
178 """
179 _shared_state = {}
180
181 def __init__(self):
182 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800183 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200184
185 @property
186 def pipe(self):
187 return self._pipe
188
189 @pipe.setter
190 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800191 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200192 raise Exception("Internal error - pipe should only be set once.")
193 self._pipe = pipe
194
juraj.linkes40dd73b2018-09-21 13:55:16 +0200195 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200196 """
197 Write current test tmpdir & desc to keep-alive pipe to signal liveness
198 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200199 if self.pipe is None:
200 # if not running forked..
201 return
202
Klement Sekera909a6a12017-08-08 04:33:53 +0200203 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200204 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200205 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200206 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200207
Dave Wallacee2efd122017-09-30 22:04:21 -0400208 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200209
210
Damjan Marionf56b77a2016-10-03 19:44:57 +0200211class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100212 """This subclass is a base class for VPP test cases that are implemented as
213 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200214 """
215
Ole Troana45dc072018-12-21 16:04:22 +0100216 extra_vpp_punt_config = []
217 extra_vpp_plugin_config = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100218
Klement Sekeraf62ae122016-10-11 11:47:09 +0200219 @property
220 def packet_infos(self):
221 """List of packet infos"""
222 return self._packet_infos
223
Klement Sekeradab231a2016-12-21 08:50:14 +0100224 @classmethod
225 def get_packet_count_for_if_idx(cls, dst_if_index):
226 """Get the number of packet info for specified destination if index"""
227 if dst_if_index in cls._packet_count_for_dst_if_idx:
228 return cls._packet_count_for_dst_if_idx[dst_if_index]
229 else:
230 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200231
232 @classmethod
233 def instance(cls):
234 """Return the instance of this testcase"""
235 return cls.test_instance
236
Damjan Marionf56b77a2016-10-03 19:44:57 +0200237 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200238 def set_debug_flags(cls, d):
239 cls.debug_core = False
240 cls.debug_gdb = False
241 cls.debug_gdbserver = False
242 if d is None:
243 return
244 dl = d.lower()
245 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200246 cls.debug_core = True
247 elif dl == "gdb":
248 cls.debug_gdb = True
249 elif dl == "gdbserver":
250 cls.debug_gdbserver = True
251 else:
252 raise Exception("Unrecognized DEBUG option: '%s'" % d)
253
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800254 @staticmethod
255 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200256 cpu_usage_list = [set(range(psutil.cpu_count()))]
257 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
258 if 'vpp_main' == p.info['name']]
259 for vpp_process in vpp_processes:
260 for cpu_usage_set in cpu_usage_list:
261 try:
262 cpu_num = vpp_process.cpu_num()
263 if cpu_num in cpu_usage_set:
264 cpu_usage_set_index = cpu_usage_list.index(
265 cpu_usage_set)
266 if cpu_usage_set_index == len(cpu_usage_list) - 1:
267 cpu_usage_list.append({cpu_num})
268 else:
269 cpu_usage_list[cpu_usage_set_index + 1].add(
270 cpu_num)
271 cpu_usage_set.remove(cpu_num)
272 break
273 except psutil.NoSuchProcess:
274 pass
275
276 for cpu_usage_set in cpu_usage_list:
277 if len(cpu_usage_set) > 0:
278 min_usage_set = cpu_usage_set
279 break
280
281 return random.choice(tuple(min_usage_set))
282
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800283 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200284 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200285 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100286 s = os.getenv("STEP", "n")
287 cls.step = True if s.lower() in ("y", "yes", "1") else False
288 d = os.getenv("DEBUG", None)
289 c = os.getenv("CACHE_OUTPUT", "1")
290 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200291 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100292 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
293 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400294 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100295 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
296 plugin_path = None
297 if cls.plugin_path is not None:
298 if cls.extern_plugin_path is not None:
299 plugin_path = "%s:%s" % (
300 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100301 else:
302 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100303 elif cls.extern_plugin_path is not None:
304 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100305 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100306 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100307 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100308 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100309 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100310 if size is not None:
311 coredump_size = "coredump-size %s" % size
312 if coredump_size is None:
313 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200314
Ole Troana45dc072018-12-21 16:04:22 +0100315 cpu_core_number = cls.get_least_used_cpu()
juraj.linkes184870a2018-07-16 14:22:01 +0200316
Ole Troana45dc072018-12-21 16:04:22 +0100317 cls.vpp_cmdline = [cls.vpp_bin, "unix",
318 "{", "nodaemon", debug_cli, "full-coredump",
319 coredump_size, "runtime-dir", cls.tempdir, "}",
320 "api-trace", "{", "on", "}", "api-segment", "{",
321 "prefix", cls.shm_prefix, "}", "cpu", "{",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200322 "main-core", str(cpu_core_number), "}",
323 "statseg", "{", "socket-name", cls.stats_sock, "}",
324 "socksvr", "{", "socket-name", cls.api_sock, "}",
325 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100326 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200327 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100328 "}", "plugin", "unittest_plugin.so", "{", "enable",
329 "}"] + cls.extra_vpp_plugin_config + ["}", ]
330 if cls.extra_vpp_punt_config is not None:
331 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100332 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100333 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400334 if cls.test_plugin_path is not None:
335 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
336
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100337 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
338 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200339
340 @classmethod
341 def wait_for_enter(cls):
342 if cls.debug_gdbserver:
343 print(double_line_delim)
344 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
345 elif cls.debug_gdb:
346 print(double_line_delim)
347 print("Spawned VPP with PID: %d" % cls.vpp.pid)
348 else:
349 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
350 return
351 print(single_line_delim)
352 print("You can debug the VPP using e.g.:")
353 if cls.debug_gdbserver:
354 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
355 print("Now is the time to attach a gdb by running the above "
356 "command, set up breakpoints etc. and then resume VPP from "
357 "within gdb by issuing the 'continue' command")
358 elif cls.debug_gdb:
359 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
360 print("Now is the time to attach a gdb by running the above "
361 "command and set up breakpoints etc.")
362 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800363 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200364
365 @classmethod
366 def run_vpp(cls):
367 cmdline = cls.vpp_cmdline
368
369 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100370 gdbserver = '/usr/bin/gdbserver'
371 if not os.path.isfile(gdbserver) or \
372 not os.access(gdbserver, os.X_OK):
373 raise Exception("gdbserver binary '%s' does not exist or is "
374 "not executable" % gdbserver)
375
376 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200377 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
378
Klement Sekera931be3a2016-11-03 05:36:01 +0100379 try:
380 cls.vpp = subprocess.Popen(cmdline,
381 stdout=subprocess.PIPE,
382 stderr=subprocess.PIPE,
383 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800384 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800385 cls.logger.critical("Subprocess returned with non-0 return code: ("
386 "%s)", e.returncode)
387 raise
388 except OSError as e:
389 cls.logger.critical("Subprocess returned with OS error: "
390 "(%s) %s", e.errno, e.strerror)
391 raise
392 except Exception as e:
393 cls.logger.exception("Subprocess returned unexpected from "
394 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100395 raise
396
Klement Sekera277b89c2016-10-28 13:20:27 +0200397 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100398
Damjan Marionf56b77a2016-10-03 19:44:57 +0200399 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200400 def wait_for_stats_socket(cls):
401 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800402 ok = False
403 while time.time() < deadline or \
404 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200405 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800406 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200407 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700408 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800409 if not ok:
410 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200411
412 @classmethod
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400413 def wait_for_coredump(cls):
414 corefile = cls.tempdir + "/core"
415 if os.path.isfile(corefile):
416 cls.logger.error("Waiting for coredump to complete: %s", corefile)
417 curr_size = os.path.getsize(corefile)
418 deadline = time.time() + 60
419 ok = False
420 while time.time() < deadline:
421 cls.sleep(1)
422 size = curr_size
423 curr_size = os.path.getsize(corefile)
424 if size == curr_size:
425 ok = True
426 break
427 if not ok:
428 cls.logger.error("Timed out waiting for coredump to complete:"
429 " %s", corefile)
430 else:
431 cls.logger.error("Coredump complete: %s, size %d",
432 corefile, curr_size)
433
434 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200435 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200436 """
437 Perform class setup before running the testcase
438 Remove shared memory files, start vpp and connect the vpp-api
439 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800440 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100441 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100442 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100443 cls.logger = get_logger(cls.__name__)
444 if hasattr(cls, 'parallel_handler'):
445 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100446 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700447
Klement Sekeraf62ae122016-10-11 11:47:09 +0200448 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200449 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200450 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200451 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200452 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
453 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100454 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
455 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200456 cls.file_handler.setLevel(DEBUG)
457 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700458 cls.logger.debug("--- setUpClass() for %s called ---" %
459 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200460 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200461 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200462 cls.logger.info("Temporary dir is %s, shm prefix is %s",
463 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200464 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100465 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100466 cls._captures = []
467 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200468 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100469 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100470 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200471 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200472 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200473 # need to catch exceptions here because if we raise, then the cleanup
474 # doesn't get called and we might end with a zombie vpp
475 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200476 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200477 cls.reporter.send_keep_alive(cls, 'setUpClass')
478 VppTestResult.current_test_case_info = TestCaseInfo(
479 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100480 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100481 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100482 cls.pump_thread_stop_flag = Event()
483 cls.pump_thread_wakeup_pipe = os.pipe()
484 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100485 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100486 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200487 if cls.debug_gdb or cls.debug_gdbserver:
488 read_timeout = 0
489 else:
490 read_timeout = 5
491 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
492 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100493 if cls.step:
494 hook = StepHook(cls)
495 else:
496 hook = PollHook(cls)
497 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200498 cls.wait_for_stats_socket()
499 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200500 try:
501 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100502 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200503 cls.vpp_startup_failed = True
504 cls.logger.critical(
505 "VPP died shortly after startup, check the"
506 " output to standard error for possible cause")
507 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100508 try:
509 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100510 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100511 try:
512 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100513 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100514 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100515 if cls.debug_gdbserver:
516 print(colorize("You're running VPP inside gdbserver but "
517 "VPP-API connection failed, did you forget "
518 "to 'continue' VPP from within gdb?", RED))
519 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100520 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100521 try:
522 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100523 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100524 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100525 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200526
Damjan Marionf56b77a2016-10-03 19:44:57 +0200527 @classmethod
528 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200529 """
530 Disconnect vpp-api, kill vpp and cleanup shared memory files
531 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200532 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
533 cls.vpp.poll()
534 if cls.vpp.returncode is None:
535 print(double_line_delim)
536 print("VPP or GDB server is still running")
537 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800538 input("When done debugging, press ENTER to kill the "
539 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200540
juraj.linkes184870a2018-07-16 14:22:01 +0200541 # first signal that we want to stop the pump thread, then wake it up
542 if hasattr(cls, 'pump_thread_stop_flag'):
543 cls.pump_thread_stop_flag.set()
544 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100545 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100546 if hasattr(cls, 'pump_thread'):
547 cls.logger.debug("Waiting for pump thread to stop")
548 cls.pump_thread.join()
549 if hasattr(cls, 'vpp_stderr_reader_thread'):
550 cls.logger.debug("Waiting for stdderr pump to stop")
551 cls.vpp_stderr_reader_thread.join()
552
Klement Sekeraf62ae122016-10-11 11:47:09 +0200553 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100554 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700555 cls.logger.debug("Disconnecting class vapi client on %s",
556 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100557 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700558 cls.logger.debug("Deleting class vapi attribute on %s",
559 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100560 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200561 cls.vpp.poll()
562 if cls.vpp.returncode is None:
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400563 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100564 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400565 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100566 cls.logger.debug("Waiting for vpp to die")
567 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700568 cls.logger.debug("Deleting class vpp attribute on %s",
569 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200570 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200571
Klement Sekera3747c752017-04-10 06:30:17 +0200572 if cls.vpp_startup_failed:
573 stdout_log = cls.logger.info
574 stderr_log = cls.logger.critical
575 else:
576 stdout_log = cls.logger.info
577 stderr_log = cls.logger.info
578
Klement Sekerae4504c62016-12-08 10:16:41 +0100579 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200580 stdout_log(single_line_delim)
581 stdout_log('VPP output to stdout while running %s:', cls.__name__)
582 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100583 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200584 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
585 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200586 stdout_log('\n%s', vpp_output)
587 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200588
Klement Sekerae4504c62016-12-08 10:16:41 +0100589 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200590 stderr_log(single_line_delim)
591 stderr_log('VPP output to stderr while running %s:', cls.__name__)
592 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100593 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200594 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
595 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200596 stderr_log('\n%s', vpp_output)
597 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200598
Damjan Marionf56b77a2016-10-03 19:44:57 +0200599 @classmethod
600 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200601 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700602 cls.logger.debug("--- tearDownClass() for %s called ---" %
603 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200604 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200605 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200606 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100607 cls.reset_packet_infos()
608 if debug_framework:
609 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200610
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700611 def show_commands_at_teardown(self):
612 """ Allow subclass specific teardown logging additions."""
613 self.logger.info("--- No test specific show commands provided. ---")
614
Damjan Marionf56b77a2016-10-03 19:44:57 +0200615 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200616 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100617 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
618 (self.__class__.__name__, self._testMethodName,
619 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700620
621 try:
622 if not self.vpp_dead:
623 self.logger.debug(self.vapi.cli("show trace max 1000"))
624 self.logger.info(self.vapi.ppcli("show interface"))
625 self.logger.info(self.vapi.ppcli("show hardware"))
626 self.logger.info(self.statistics.set_errors_str())
627 self.logger.info(self.vapi.ppcli("show run"))
628 self.logger.info(self.vapi.ppcli("show log"))
629 self.logger.info("Logging testcase specific show commands.")
630 self.show_commands_at_teardown()
631 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500632 # Save/Dump VPP api trace log
633 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
634 tmp_api_trace = "/tmp/%s" % api_trace
635 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
636 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
637 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
638 vpp_api_trace_log))
639 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500640 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500641 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700642 except VppTransportShmemIOError:
643 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
644 "Cannot log show commands.")
645 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100646 else:
647 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200648
Damjan Marionf56b77a2016-10-03 19:44:57 +0200649 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200650 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800651 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200652 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100653 if self.vpp_dead:
654 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100655 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100656 self.vpp_stdout_deque.append(
657 "--- test setUp() for %s.%s(%s) starts here ---\n" %
658 (self.__class__.__name__, self._testMethodName,
659 self._testMethodDoc))
660 self.vpp_stderr_deque.append(
661 "--- test setUp() for %s.%s(%s) starts here ---\n" %
662 (self.__class__.__name__, self._testMethodName,
663 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200664 self.vapi.cli("clear trace")
665 # store the test instance inside the test class - so that objects
666 # holding the class can access instance methods (like assertEqual)
667 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200668
Damjan Marionf56b77a2016-10-03 19:44:57 +0200669 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200670 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200671 """
672 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200673
Klement Sekera75e7d132017-09-20 08:26:30 +0200674 :param interfaces: iterable interface indexes (if None,
675 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200676
Klement Sekeraf62ae122016-10-11 11:47:09 +0200677 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200678 if interfaces is None:
679 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200680 for i in interfaces:
681 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200682
Damjan Marionf56b77a2016-10-03 19:44:57 +0200683 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100684 def register_capture(cls, cap_name):
685 """ Register a capture in the testclass """
686 # add to the list of captures with current timestamp
687 cls._captures.append((time.time(), cap_name))
688 # filter out from zombies
689 cls._zombie_captures = [(stamp, name)
690 for (stamp, name) in cls._zombie_captures
691 if name != cap_name]
692
693 @classmethod
694 def pg_start(cls):
695 """ Remove any zombie captures and enable the packet generator """
696 # how long before capture is allowed to be deleted - otherwise vpp
697 # crashes - 100ms seems enough (this shouldn't be needed at all)
698 capture_ttl = 0.1
699 now = time.time()
700 for stamp, cap_name in cls._zombie_captures:
701 wait = stamp + capture_ttl - now
702 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100703 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100704 now = time.time()
705 cls.logger.debug("Removing zombie capture %s" % cap_name)
706 cls.vapi.cli('packet-generator delete %s' % cap_name)
707
Klement Sekerad91fa612019-01-15 13:25:09 +0100708 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200709 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100710 cls._zombie_captures = cls._captures
711 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200712
Damjan Marionf56b77a2016-10-03 19:44:57 +0200713 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200714 def create_pg_interfaces(cls, interfaces):
715 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100716 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200717
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100718 :param interfaces: iterable indexes of the interfaces.
719 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200720
Klement Sekeraf62ae122016-10-11 11:47:09 +0200721 """
722 result = []
723 for i in interfaces:
724 intf = VppPGInterface(cls, i)
725 setattr(cls, intf.name, intf)
726 result.append(intf)
727 cls.pg_interfaces = result
728 return result
729
Matej Klotton0178d522016-11-04 11:11:44 +0100730 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200731 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100732 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100733 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100734
Klement Sekerab9ef2732018-06-24 22:49:33 +0200735 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100736 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100737 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200738 result = [VppLoInterface(cls) for i in range(count)]
739 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100740 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100741 cls.lo_interfaces = result
742 return result
743
Neale Ranns192b13f2019-03-15 02:16:20 -0700744 @classmethod
745 def create_bvi_interfaces(cls, count):
746 """
747 Create BVI interfaces.
748
749 :param count: number of interfaces created.
750 :returns: List of created interfaces.
751 """
752 result = [VppBviInterface(cls) for i in range(count)]
753 for intf in result:
754 setattr(cls, intf.name, intf)
755 cls.bvi_interfaces = result
756 return result
757
Damjan Marionf56b77a2016-10-03 19:44:57 +0200758 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200759 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200760 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200761 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200762 NOTE: Currently works only when Raw layer is present.
763
764 :param packet: packet
765 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200766 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200767
768 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769 packet_len = len(packet) + 4
770 extend = size - packet_len
771 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200772 num = (extend // len(padding)) + 1
773 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200774
Klement Sekeradab231a2016-12-21 08:50:14 +0100775 @classmethod
776 def reset_packet_infos(cls):
777 """ Reset the list of packet info objects and packet counts to zero """
778 cls._packet_infos = {}
779 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200780
Klement Sekeradab231a2016-12-21 08:50:14 +0100781 @classmethod
782 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200783 """
784 Create packet info object containing the source and destination indexes
785 and add it to the testcase's packet info list
786
Klement Sekeradab231a2016-12-21 08:50:14 +0100787 :param VppInterface src_if: source interface
788 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200789
790 :returns: _PacketInfo object
791
792 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200793 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100794 info.index = len(cls._packet_infos)
795 info.src = src_if.sw_if_index
796 info.dst = dst_if.sw_if_index
797 if isinstance(dst_if, VppSubInterface):
798 dst_idx = dst_if.parent.sw_if_index
799 else:
800 dst_idx = dst_if.sw_if_index
801 if dst_idx in cls._packet_count_for_dst_if_idx:
802 cls._packet_count_for_dst_if_idx[dst_idx] += 1
803 else:
804 cls._packet_count_for_dst_if_idx[dst_idx] = 1
805 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200806 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200807
Damjan Marionf56b77a2016-10-03 19:44:57 +0200808 @staticmethod
809 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200810 """
811 Convert _PacketInfo object to packet payload
812
813 :param info: _PacketInfo object
814
815 :returns: string containing serialized data from packet info
816 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100817 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
818 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200819
Damjan Marionf56b77a2016-10-03 19:44:57 +0200820 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800821 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200822 """
823 Convert packet payload to _PacketInfo object
824
825 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700826 :type payload: <class 'scapy.packet.Raw'>
827 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800828 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700829 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200830 :returns: _PacketInfo object containing de-serialized data from payload
831
832 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800833 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200834 info = _PacketInfo()
835 info.index = int(numbers[0])
836 info.src = int(numbers[1])
837 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100838 info.ip = int(numbers[3])
839 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200840 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200841
Damjan Marionf56b77a2016-10-03 19:44:57 +0200842 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200843 """
844 Iterate over the packet info list stored in the testcase
845 Start iteration with first element if info is None
846 Continue based on index in info if info is specified
847
848 :param info: info or None
849 :returns: next info in list or None if no more infos
850 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200851 if info is None:
852 next_index = 0
853 else:
854 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100855 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200856 return None
857 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100858 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200859
Klement Sekeraf62ae122016-10-11 11:47:09 +0200860 def get_next_packet_info_for_interface(self, src_index, info):
861 """
862 Search the packet info list for the next packet info with same source
863 interface index
864
865 :param src_index: source interface index to search for
866 :param info: packet info - where to start the search
867 :returns: packet info or None
868
869 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200870 while True:
871 info = self.get_next_packet_info(info)
872 if info is None:
873 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200874 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200875 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200876
Klement Sekeraf62ae122016-10-11 11:47:09 +0200877 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
878 """
879 Search the packet info list for the next packet info with same source
880 and destination interface indexes
881
882 :param src_index: source interface index to search for
883 :param dst_index: destination interface index to search for
884 :param info: packet info - where to start the search
885 :returns: packet info or None
886
887 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200888 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200889 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200890 if info is None:
891 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200892 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200893 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200894
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200895 def assert_equal(self, real_value, expected_value, name_or_class=None):
896 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100897 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200898 return
899 try:
900 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
901 msg = msg % (getdoc(name_or_class).strip(),
902 real_value, str(name_or_class(real_value)),
903 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100904 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200905 msg = "Invalid %s: %s does not match expected value %s" % (
906 name_or_class, real_value, expected_value)
907
908 self.assertEqual(real_value, expected_value, msg)
909
Klement Sekerab17dd962017-01-09 07:43:48 +0100910 def assert_in_range(self,
911 real_value,
912 expected_min,
913 expected_max,
914 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200915 if name is None:
916 msg = None
917 else:
918 msg = "Invalid %s: %s out of range <%s,%s>" % (
919 name, real_value, expected_min, expected_max)
920 self.assertTrue(expected_min <= real_value <= expected_max, msg)
921
Klement Sekerad81ae412018-05-16 10:52:54 +0200922 def assert_packet_checksums_valid(self, packet,
923 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700924 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200925 self.logger.debug(
926 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200927 udp_layers = ['UDP', 'UDPerror']
928 checksum_fields = ['cksum', 'chksum']
929 checksums = []
930 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700931 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200932 while True:
933 layer = temp.getlayer(counter)
934 if layer:
935 for cf in checksum_fields:
936 if hasattr(layer, cf):
937 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +0200938 0 == getattr(layer, cf) and \
939 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200940 continue
941 delattr(layer, cf)
942 checksums.append((counter, cf))
943 else:
944 break
945 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200946 if 0 == len(checksums):
947 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700948 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200949 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200950 calc_sum = getattr(temp[layer], cf)
951 self.assert_equal(
952 getattr(received[layer], cf), calc_sum,
953 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
954 self.logger.debug(
955 "Checksum field `%s` on `%s` layer has correct value `%s`" %
956 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200957
958 def assert_checksum_valid(self, received_packet, layer,
959 field_name='chksum',
960 ignore_zero_checksum=False):
961 """ Check checksum of received packet on given layer """
962 received_packet_checksum = getattr(received_packet[layer], field_name)
963 if ignore_zero_checksum and 0 == received_packet_checksum:
964 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700965 recalculated = received_packet.__class__(
966 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200967 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700968 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +0200969 self.assert_equal(received_packet_checksum,
970 getattr(recalculated[layer], field_name),
971 "packet checksum on layer: %s" % layer)
972
973 def assert_ip_checksum_valid(self, received_packet,
974 ignore_zero_checksum=False):
975 self.assert_checksum_valid(received_packet, 'IP',
976 ignore_zero_checksum=ignore_zero_checksum)
977
978 def assert_tcp_checksum_valid(self, received_packet,
979 ignore_zero_checksum=False):
980 self.assert_checksum_valid(received_packet, 'TCP',
981 ignore_zero_checksum=ignore_zero_checksum)
982
983 def assert_udp_checksum_valid(self, received_packet,
984 ignore_zero_checksum=True):
985 self.assert_checksum_valid(received_packet, 'UDP',
986 ignore_zero_checksum=ignore_zero_checksum)
987
988 def assert_embedded_icmp_checksum_valid(self, received_packet):
989 if received_packet.haslayer(IPerror):
990 self.assert_checksum_valid(received_packet, 'IPerror')
991 if received_packet.haslayer(TCPerror):
992 self.assert_checksum_valid(received_packet, 'TCPerror')
993 if received_packet.haslayer(UDPerror):
994 self.assert_checksum_valid(received_packet, 'UDPerror',
995 ignore_zero_checksum=True)
996 if received_packet.haslayer(ICMPerror):
997 self.assert_checksum_valid(received_packet, 'ICMPerror')
998
999 def assert_icmp_checksum_valid(self, received_packet):
1000 self.assert_checksum_valid(received_packet, 'ICMP')
1001 self.assert_embedded_icmp_checksum_valid(received_packet)
1002
1003 def assert_icmpv6_checksum_valid(self, pkt):
1004 if pkt.haslayer(ICMPv6DestUnreach):
1005 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1006 self.assert_embedded_icmp_checksum_valid(pkt)
1007 if pkt.haslayer(ICMPv6EchoRequest):
1008 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1009 if pkt.haslayer(ICMPv6EchoReply):
1010 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1011
Klement Sekera3a343d42019-05-16 14:35:46 +02001012 def get_packet_counter(self, counter):
1013 if counter.startswith("/"):
1014 counter_value = self.statistics.get_counter(counter)
1015 else:
1016 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001017 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001018 for i in range(1, len(counters) - 1):
1019 results = counters[i].split()
1020 if results[1] == counter:
1021 counter_value = int(results[0])
1022 break
1023 return counter_value
1024
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001025 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001026 counter_value = self.get_packet_counter(counter)
1027 self.assert_equal(counter_value, expected_value,
1028 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001029
Ole Troan233e4682019-05-16 15:01:34 +02001030 def assert_error_counter_equal(self, counter, expected_value):
1031 counter_value = self.statistics.get_err_counter(counter)
1032 self.assert_equal(counter_value, expected_value,
1033 "error counter `%s'" % counter)
1034
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001035 @classmethod
1036 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001037
1038 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1039 # * by Guido, only the main thread can be interrupted.
1040 # */
1041 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1042 if timeout == 0:
1043 # yield quantum
1044 if hasattr(os, 'sched_yield'):
1045 os.sched_yield()
1046 else:
1047 time.sleep(0)
1048 return
1049
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001050 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001051 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001052 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001053 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001054 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001055 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001056 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001057 "slept for %es instead of ~%es!",
1058 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001059 if hasattr(cls, 'logger'):
1060 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001061 "Finished sleep (%s) - slept %es (wanted %es)",
1062 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001063
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001064 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001065 self.vapi.cli("clear trace")
1066 intf.add_stream(pkts)
1067 self.pg_enable_capture(self.pg_interfaces)
1068 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001069
1070 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1071 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001072 if not timeout:
1073 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001074 for i in self.pg_interfaces:
1075 i.get_capture(0, timeout=timeout)
1076 i.assert_nothing_captured(remark=remark)
1077 timeout = 0.1
1078
Neale Rannsd7603d92019-03-28 08:56:10 +00001079 def send_and_expect(self, intf, pkts, output, n_rx=None):
1080 if not n_rx:
1081 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001082 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001083 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001084 return rx
1085
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001086 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1087 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001088 rx = output.get_capture(len(pkts))
1089 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001090 if not timeout:
1091 timeout = 1
1092 for i in self.pg_interfaces:
1093 if i not in outputs:
1094 i.get_capture(0, timeout=timeout)
1095 i.assert_nothing_captured()
1096 timeout = 0.1
1097
Neale Ranns52fae862018-01-08 04:41:42 -08001098 return rx
1099
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001100 def runTest(self):
1101 """ unittest calls runTest when TestCase is instantiated without a
1102 test case. Use case: Writing unittests against VppTestCase"""
1103 pass
1104
Damjan Marionf56b77a2016-10-03 19:44:57 +02001105
juraj.linkes184870a2018-07-16 14:22:01 +02001106def get_testcase_doc_name(test):
1107 return getdoc(test.__class__).splitlines()[0]
1108
1109
Ole Trøan5ba91592018-11-22 10:01:09 +00001110def get_test_description(descriptions, test):
1111 short_description = test.shortDescription()
1112 if descriptions and short_description:
1113 return short_description
1114 else:
1115 return str(test)
1116
1117
juraj.linkes40dd73b2018-09-21 13:55:16 +02001118class TestCaseInfo(object):
1119 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1120 self.logger = logger
1121 self.tempdir = tempdir
1122 self.vpp_pid = vpp_pid
1123 self.vpp_bin_path = vpp_bin_path
1124 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001125
1126
Damjan Marionf56b77a2016-10-03 19:44:57 +02001127class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001128 """
1129 @property result_string
1130 String variable to store the test case result string.
1131 @property errors
1132 List variable containing 2-tuples of TestCase instances and strings
1133 holding formatted tracebacks. Each tuple represents a test which
1134 raised an unexpected exception.
1135 @property failures
1136 List variable containing 2-tuples of TestCase instances and strings
1137 holding formatted tracebacks. Each tuple represents a test where
1138 a failure was explicitly signalled using the TestCase.assert*()
1139 methods.
1140 """
1141
juraj.linkes40dd73b2018-09-21 13:55:16 +02001142 failed_test_cases_info = set()
1143 core_crash_test_cases_info = set()
1144 current_test_case_info = None
1145
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001146 def __init__(self, stream=None, descriptions=None, verbosity=None,
1147 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001148 """
Klement Sekerada505f62017-01-04 12:58:53 +01001149 :param stream File descriptor to store where to report test results.
1150 Set to the standard error stream by default.
1151 :param descriptions Boolean variable to store information if to use
1152 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001153 :param verbosity Integer variable to store required verbosity level.
1154 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001155 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001156 self.stream = stream
1157 self.descriptions = descriptions
1158 self.verbosity = verbosity
1159 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001160 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001161
Damjan Marionf56b77a2016-10-03 19:44:57 +02001162 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001163 """
1164 Record a test succeeded result
1165
1166 :param test:
1167
1168 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001169 if self.current_test_case_info:
1170 self.current_test_case_info.logger.debug(
1171 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1172 test._testMethodName,
1173 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001174 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001175 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001176
juraj.linkescae64f82018-09-19 15:01:47 +02001177 self.send_result_through_pipe(test, PASS)
1178
Klement Sekeraf62ae122016-10-11 11:47:09 +02001179 def addSkip(self, test, reason):
1180 """
1181 Record a test skipped.
1182
1183 :param test:
1184 :param reason:
1185
1186 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001187 if self.current_test_case_info:
1188 self.current_test_case_info.logger.debug(
1189 "--- addSkip() %s.%s(%s) called, reason is %s" %
1190 (test.__class__.__name__, test._testMethodName,
1191 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001192 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001193 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001194
juraj.linkescae64f82018-09-19 15:01:47 +02001195 self.send_result_through_pipe(test, SKIP)
1196
juraj.linkes40dd73b2018-09-21 13:55:16 +02001197 def symlink_failed(self):
1198 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001199 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001200 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001201 link_path = os.path.join(
1202 failed_dir,
1203 '%s-FAILED' %
1204 os.path.basename(self.current_test_case_info.tempdir))
1205 if self.current_test_case_info.logger:
1206 self.current_test_case_info.logger.debug(
1207 "creating a link to the failed test")
1208 self.current_test_case_info.logger.debug(
1209 "os.symlink(%s, %s)" %
1210 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001211 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001212 if self.current_test_case_info.logger:
1213 self.current_test_case_info.logger.debug(
1214 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001215 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001216 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001217
Klement Sekeraf413bef2017-08-15 07:09:02 +02001218 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001219 if self.current_test_case_info.logger:
1220 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001221
juraj.linkescae64f82018-09-19 15:01:47 +02001222 def send_result_through_pipe(self, test, result):
1223 if hasattr(self, 'test_framework_result_pipe'):
1224 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001225 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001226 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001227
juraj.linkes40dd73b2018-09-21 13:55:16 +02001228 def log_error(self, test, err, fn_name):
1229 if self.current_test_case_info:
1230 if isinstance(test, unittest.suite._ErrorHolder):
1231 test_name = test.description
1232 else:
1233 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1234 test._testMethodName,
1235 test._testMethodDoc)
1236 self.current_test_case_info.logger.debug(
1237 "--- %s() %s called, err is %s" %
1238 (fn_name, test_name, err))
1239 self.current_test_case_info.logger.debug(
1240 "formatted exception is:\n%s" %
1241 "".join(format_exception(*err)))
1242
1243 def add_error(self, test, err, unittest_fn, error_type):
1244 if error_type == FAIL:
1245 self.log_error(test, err, 'addFailure')
1246 error_type_str = colorize("FAIL", RED)
1247 elif error_type == ERROR:
1248 self.log_error(test, err, 'addError')
1249 error_type_str = colorize("ERROR", RED)
1250 else:
1251 raise Exception('Error type %s cannot be used to record an '
1252 'error or a failure' % error_type)
1253
1254 unittest_fn(self, test, err)
1255 if self.current_test_case_info:
1256 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1257 (error_type_str,
1258 self.current_test_case_info.tempdir)
1259 self.symlink_failed()
1260 self.failed_test_cases_info.add(self.current_test_case_info)
1261 if is_core_present(self.current_test_case_info.tempdir):
1262 if not self.current_test_case_info.core_crash_test:
1263 if isinstance(test, unittest.suite._ErrorHolder):
1264 test_name = str(test)
1265 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001266 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001267 get_testcase_doc_name(test), test.id())
1268 self.current_test_case_info.core_crash_test = test_name
1269 self.core_crash_test_cases_info.add(
1270 self.current_test_case_info)
1271 else:
1272 self.result_string = '%s [no temp dir]' % error_type_str
1273
1274 self.send_result_through_pipe(test, error_type)
1275
Damjan Marionf56b77a2016-10-03 19:44:57 +02001276 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001277 """
1278 Record a test failed result
1279
1280 :param test:
1281 :param err: error message
1282
1283 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001284 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001285
Damjan Marionf56b77a2016-10-03 19:44:57 +02001286 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001287 """
1288 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001289
Klement Sekeraf62ae122016-10-11 11:47:09 +02001290 :param test:
1291 :param err: error message
1292
1293 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001294 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001295
Damjan Marionf56b77a2016-10-03 19:44:57 +02001296 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001297 """
1298 Get test description
1299
1300 :param test:
1301 :returns: test description
1302
1303 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001304 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001305
Damjan Marionf56b77a2016-10-03 19:44:57 +02001306 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001307 """
1308 Start a test
1309
1310 :param test:
1311
1312 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001313
1314 def print_header(test):
1315 if not hasattr(test.__class__, '_header_printed'):
1316 print(double_line_delim)
1317 print(colorize(getdoc(test).splitlines()[0], GREEN))
1318 print(double_line_delim)
1319 test.__class__._header_printed = True
1320
1321 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001322
Damjan Marionf56b77a2016-10-03 19:44:57 +02001323 unittest.TestResult.startTest(self, test)
1324 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001325 self.stream.writeln(
1326 "Starting " + self.getDescription(test) + " ...")
1327 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001328
Damjan Marionf56b77a2016-10-03 19:44:57 +02001329 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001330 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001331 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001332
1333 :param test:
1334
1335 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001336 unittest.TestResult.stopTest(self, test)
1337 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001338 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001339 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001340 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001341 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001342 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001343 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001344 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001345
1346 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001347
Damjan Marionf56b77a2016-10-03 19:44:57 +02001348 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001349 """
1350 Print errors from running the test case
1351 """
juraj.linkesabec0122018-11-16 17:28:56 +01001352 if len(self.errors) > 0 or len(self.failures) > 0:
1353 self.stream.writeln()
1354 self.printErrorList('ERROR', self.errors)
1355 self.printErrorList('FAIL', self.failures)
1356
1357 # ^^ that is the last output from unittest before summary
1358 if not self.runner.print_summary:
1359 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1360 self.stream = devnull
1361 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001362
Damjan Marionf56b77a2016-10-03 19:44:57 +02001363 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001364 """
1365 Print error list to the output stream together with error type
1366 and test case description.
1367
1368 :param flavour: error type
1369 :param errors: iterable errors
1370
1371 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001372 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001373 self.stream.writeln(double_line_delim)
1374 self.stream.writeln("%s: %s" %
1375 (flavour, self.getDescription(test)))
1376 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001377 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001378
1379
Damjan Marionf56b77a2016-10-03 19:44:57 +02001380class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001381 """
Klement Sekera104543f2017-02-03 07:29:43 +01001382 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001383 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001384
Klement Sekeraf62ae122016-10-11 11:47:09 +02001385 @property
1386 def resultclass(self):
1387 """Class maintaining the results of the tests"""
1388 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001389
juraj.linkes184870a2018-07-16 14:22:01 +02001390 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001391 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001392 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001393 # ignore stream setting here, use hard-coded stdout to be in sync
1394 # with prints from VppTestCase methods ...
1395 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1396 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001397 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001398 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001399
juraj.linkesabec0122018-11-16 17:28:56 +01001400 self.orig_stream = self.stream
1401 self.resultclass.test_framework_result_pipe = result_pipe
1402
1403 self.print_summary = print_summary
1404
1405 def _makeResult(self):
1406 return self.resultclass(self.stream,
1407 self.descriptions,
1408 self.verbosity,
1409 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001410
Damjan Marionf56b77a2016-10-03 19:44:57 +02001411 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001412 """
1413 Run the tests
1414
1415 :param test:
1416
1417 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001418 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001419
1420 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001421 if not self.print_summary:
1422 self.stream = self.orig_stream
1423 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001424 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001425
1426
1427class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001428 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001429 self.logger = logger
1430 self.args = args
1431 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001432 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001433 super(Worker, self).__init__()
1434
1435 def run(self):
1436 executable = self.args[0]
1437 self.logger.debug("Running executable w/args `%s'" % self.args)
1438 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001439 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001440 env["CK_LOG_FILE_NAME"] = "-"
1441 self.process = subprocess.Popen(
1442 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1443 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1444 out, err = self.process.communicate()
1445 self.logger.debug("Finished running `%s'" % executable)
1446 self.logger.info("Return code is `%s'" % self.process.returncode)
1447 self.logger.info(single_line_delim)
1448 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1449 self.logger.info(single_line_delim)
1450 self.logger.info(out)
1451 self.logger.info(single_line_delim)
1452 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1453 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001454 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001455 self.logger.info(single_line_delim)
1456 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001457
Klement Sekera6aa58b72019-05-16 14:34:55 +02001458
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001459if __name__ == '__main__':
1460 pass