blob: 26e93e4abd92cadc71efb702a7f2e01c5f4531e4 [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:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400354 print("sudo gdb " + cls.vpp_bin +
355 " -ex 'target remote localhost:7777'")
Klement Sekera277b89c2016-10-28 13:20:27 +0200356 print("Now is the time to attach a gdb by running the above "
357 "command, set up breakpoints etc. and then resume VPP from "
358 "within gdb by issuing the 'continue' command")
359 elif cls.debug_gdb:
Paul Vinciguerra3a9f11e2019-06-18 22:59:55 -0400360 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
Klement Sekera277b89c2016-10-28 13:20:27 +0200361 print("Now is the time to attach a gdb by running the above "
362 "command and set up breakpoints etc.")
363 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800364 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200365
366 @classmethod
367 def run_vpp(cls):
368 cmdline = cls.vpp_cmdline
369
370 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100371 gdbserver = '/usr/bin/gdbserver'
372 if not os.path.isfile(gdbserver) or \
373 not os.access(gdbserver, os.X_OK):
374 raise Exception("gdbserver binary '%s' does not exist or is "
375 "not executable" % gdbserver)
376
377 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200378 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
379
Klement Sekera931be3a2016-11-03 05:36:01 +0100380 try:
381 cls.vpp = subprocess.Popen(cmdline,
382 stdout=subprocess.PIPE,
383 stderr=subprocess.PIPE,
384 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800385 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800386 cls.logger.critical("Subprocess returned with non-0 return code: ("
387 "%s)", e.returncode)
388 raise
389 except OSError as e:
390 cls.logger.critical("Subprocess returned with OS error: "
391 "(%s) %s", e.errno, e.strerror)
392 raise
393 except Exception as e:
394 cls.logger.exception("Subprocess returned unexpected from "
395 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100396 raise
397
Klement Sekera277b89c2016-10-28 13:20:27 +0200398 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100399
Damjan Marionf56b77a2016-10-03 19:44:57 +0200400 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200401 def wait_for_stats_socket(cls):
402 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800403 ok = False
404 while time.time() < deadline or \
405 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200406 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800407 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200408 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700409 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800410 if not ok:
411 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200412
413 @classmethod
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400414 def wait_for_coredump(cls):
415 corefile = cls.tempdir + "/core"
416 if os.path.isfile(corefile):
417 cls.logger.error("Waiting for coredump to complete: %s", corefile)
418 curr_size = os.path.getsize(corefile)
419 deadline = time.time() + 60
420 ok = False
421 while time.time() < deadline:
422 cls.sleep(1)
423 size = curr_size
424 curr_size = os.path.getsize(corefile)
425 if size == curr_size:
426 ok = True
427 break
428 if not ok:
429 cls.logger.error("Timed out waiting for coredump to complete:"
430 " %s", corefile)
431 else:
432 cls.logger.error("Coredump complete: %s, size %d",
433 corefile, curr_size)
434
435 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200436 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200437 """
438 Perform class setup before running the testcase
439 Remove shared memory files, start vpp and connect the vpp-api
440 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800441 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100442 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100443 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100444 cls.logger = get_logger(cls.__name__)
445 if hasattr(cls, 'parallel_handler'):
446 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100447 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700448
Klement Sekeraf62ae122016-10-11 11:47:09 +0200449 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200450 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200451 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200452 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200453 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
454 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100455 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
456 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200457 cls.file_handler.setLevel(DEBUG)
458 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700459 cls.logger.debug("--- setUpClass() for %s called ---" %
460 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200461 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200462 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200463 cls.logger.info("Temporary dir is %s, shm prefix is %s",
464 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200465 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100466 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100467 cls._captures = []
468 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200469 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100470 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100471 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200472 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200473 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200474 # need to catch exceptions here because if we raise, then the cleanup
475 # doesn't get called and we might end with a zombie vpp
476 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200477 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200478 cls.reporter.send_keep_alive(cls, 'setUpClass')
479 VppTestResult.current_test_case_info = TestCaseInfo(
480 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100481 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100482 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100483 cls.pump_thread_stop_flag = Event()
484 cls.pump_thread_wakeup_pipe = os.pipe()
485 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100486 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100487 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200488 if cls.debug_gdb or cls.debug_gdbserver:
489 read_timeout = 0
490 else:
491 read_timeout = 5
492 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
493 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100494 if cls.step:
495 hook = StepHook(cls)
496 else:
497 hook = PollHook(cls)
498 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200499 cls.wait_for_stats_socket()
500 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200501 try:
502 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100503 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200504 cls.vpp_startup_failed = True
505 cls.logger.critical(
506 "VPP died shortly after startup, check the"
507 " output to standard error for possible cause")
508 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100509 try:
510 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100511 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100512 try:
513 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100514 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100515 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100516 if cls.debug_gdbserver:
517 print(colorize("You're running VPP inside gdbserver but "
518 "VPP-API connection failed, did you forget "
519 "to 'continue' VPP from within gdb?", RED))
520 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100521 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100522 try:
523 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100524 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100525 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100526 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200527
Damjan Marionf56b77a2016-10-03 19:44:57 +0200528 @classmethod
529 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200530 """
531 Disconnect vpp-api, kill vpp and cleanup shared memory files
532 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200533 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
534 cls.vpp.poll()
535 if cls.vpp.returncode is None:
536 print(double_line_delim)
537 print("VPP or GDB server is still running")
538 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800539 input("When done debugging, press ENTER to kill the "
540 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200541
juraj.linkes184870a2018-07-16 14:22:01 +0200542 # first signal that we want to stop the pump thread, then wake it up
543 if hasattr(cls, 'pump_thread_stop_flag'):
544 cls.pump_thread_stop_flag.set()
545 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100546 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100547 if hasattr(cls, 'pump_thread'):
548 cls.logger.debug("Waiting for pump thread to stop")
549 cls.pump_thread.join()
550 if hasattr(cls, 'vpp_stderr_reader_thread'):
551 cls.logger.debug("Waiting for stdderr pump to stop")
552 cls.vpp_stderr_reader_thread.join()
553
Klement Sekeraf62ae122016-10-11 11:47:09 +0200554 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100555 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700556 cls.logger.debug("Disconnecting class vapi client on %s",
557 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100558 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700559 cls.logger.debug("Deleting class vapi attribute on %s",
560 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100561 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200562 cls.vpp.poll()
563 if cls.vpp.returncode is None:
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400564 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100565 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400566 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100567 cls.logger.debug("Waiting for vpp to die")
568 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700569 cls.logger.debug("Deleting class vpp attribute on %s",
570 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200571 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200572
Klement Sekera3747c752017-04-10 06:30:17 +0200573 if cls.vpp_startup_failed:
574 stdout_log = cls.logger.info
575 stderr_log = cls.logger.critical
576 else:
577 stdout_log = cls.logger.info
578 stderr_log = cls.logger.info
579
Klement Sekerae4504c62016-12-08 10:16:41 +0100580 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200581 stdout_log(single_line_delim)
582 stdout_log('VPP output to stdout while running %s:', cls.__name__)
583 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100584 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200585 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
586 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200587 stdout_log('\n%s', vpp_output)
588 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200589
Klement Sekerae4504c62016-12-08 10:16:41 +0100590 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200591 stderr_log(single_line_delim)
592 stderr_log('VPP output to stderr while running %s:', cls.__name__)
593 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100594 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200595 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
596 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200597 stderr_log('\n%s', vpp_output)
598 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200599
Damjan Marionf56b77a2016-10-03 19:44:57 +0200600 @classmethod
601 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200602 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700603 cls.logger.debug("--- tearDownClass() for %s called ---" %
604 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200605 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200606 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200607 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100608 cls.reset_packet_infos()
609 if debug_framework:
610 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200611
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700612 def show_commands_at_teardown(self):
613 """ Allow subclass specific teardown logging additions."""
614 self.logger.info("--- No test specific show commands provided. ---")
615
Damjan Marionf56b77a2016-10-03 19:44:57 +0200616 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200617 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100618 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
619 (self.__class__.__name__, self._testMethodName,
620 self._testMethodDoc))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700621
622 try:
623 if not self.vpp_dead:
624 self.logger.debug(self.vapi.cli("show trace max 1000"))
625 self.logger.info(self.vapi.ppcli("show interface"))
626 self.logger.info(self.vapi.ppcli("show hardware"))
627 self.logger.info(self.statistics.set_errors_str())
628 self.logger.info(self.vapi.ppcli("show run"))
629 self.logger.info(self.vapi.ppcli("show log"))
630 self.logger.info("Logging testcase specific show commands.")
631 self.show_commands_at_teardown()
632 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500633 # Save/Dump VPP api trace log
634 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
635 tmp_api_trace = "/tmp/%s" % api_trace
636 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
637 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
638 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
639 vpp_api_trace_log))
640 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500641 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500642 vpp_api_trace_log))
Paul Vinciguerra499ea642019-03-15 09:39:19 -0700643 except VppTransportShmemIOError:
644 self.logger.debug("VppTransportShmemIOError: Vpp dead. "
645 "Cannot log show commands.")
646 self.vpp_dead = True
Klement Sekera1b686402017-03-02 11:29:19 +0100647 else:
648 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200649
Damjan Marionf56b77a2016-10-03 19:44:57 +0200650 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200651 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800652 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200653 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100654 if self.vpp_dead:
655 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100656 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100657 self.vpp_stdout_deque.append(
658 "--- test setUp() for %s.%s(%s) starts here ---\n" %
659 (self.__class__.__name__, self._testMethodName,
660 self._testMethodDoc))
661 self.vpp_stderr_deque.append(
662 "--- test setUp() for %s.%s(%s) starts here ---\n" %
663 (self.__class__.__name__, self._testMethodName,
664 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200665 self.vapi.cli("clear trace")
666 # store the test instance inside the test class - so that objects
667 # holding the class can access instance methods (like assertEqual)
668 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200669
Damjan Marionf56b77a2016-10-03 19:44:57 +0200670 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200671 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200672 """
673 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200674
Klement Sekera75e7d132017-09-20 08:26:30 +0200675 :param interfaces: iterable interface indexes (if None,
676 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200677
Klement Sekeraf62ae122016-10-11 11:47:09 +0200678 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200679 if interfaces is None:
680 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200681 for i in interfaces:
682 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200683
Damjan Marionf56b77a2016-10-03 19:44:57 +0200684 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100685 def register_capture(cls, cap_name):
686 """ Register a capture in the testclass """
687 # add to the list of captures with current timestamp
688 cls._captures.append((time.time(), cap_name))
689 # filter out from zombies
690 cls._zombie_captures = [(stamp, name)
691 for (stamp, name) in cls._zombie_captures
692 if name != cap_name]
693
694 @classmethod
695 def pg_start(cls):
696 """ Remove any zombie captures and enable the packet generator """
697 # how long before capture is allowed to be deleted - otherwise vpp
698 # crashes - 100ms seems enough (this shouldn't be needed at all)
699 capture_ttl = 0.1
700 now = time.time()
701 for stamp, cap_name in cls._zombie_captures:
702 wait = stamp + capture_ttl - now
703 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100704 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100705 now = time.time()
706 cls.logger.debug("Removing zombie capture %s" % cap_name)
707 cls.vapi.cli('packet-generator delete %s' % cap_name)
708
Klement Sekerad91fa612019-01-15 13:25:09 +0100709 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200710 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100711 cls._zombie_captures = cls._captures
712 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200713
Damjan Marionf56b77a2016-10-03 19:44:57 +0200714 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200715 def create_pg_interfaces(cls, interfaces):
716 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100717 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200718
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100719 :param interfaces: iterable indexes of the interfaces.
720 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200721
Klement Sekeraf62ae122016-10-11 11:47:09 +0200722 """
723 result = []
724 for i in interfaces:
725 intf = VppPGInterface(cls, i)
726 setattr(cls, intf.name, intf)
727 result.append(intf)
728 cls.pg_interfaces = result
729 return result
730
Matej Klotton0178d522016-11-04 11:11:44 +0100731 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200732 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100733 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100734 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100735
Klement Sekerab9ef2732018-06-24 22:49:33 +0200736 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100737 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100738 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200739 result = [VppLoInterface(cls) for i in range(count)]
740 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100741 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100742 cls.lo_interfaces = result
743 return result
744
Neale Ranns192b13f2019-03-15 02:16:20 -0700745 @classmethod
746 def create_bvi_interfaces(cls, count):
747 """
748 Create BVI interfaces.
749
750 :param count: number of interfaces created.
751 :returns: List of created interfaces.
752 """
753 result = [VppBviInterface(cls) for i in range(count)]
754 for intf in result:
755 setattr(cls, intf.name, intf)
756 cls.bvi_interfaces = result
757 return result
758
Damjan Marionf56b77a2016-10-03 19:44:57 +0200759 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200760 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200761 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200762 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200763 NOTE: Currently works only when Raw layer is present.
764
765 :param packet: packet
766 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200767 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200768
769 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200770 packet_len = len(packet) + 4
771 extend = size - packet_len
772 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200773 num = (extend // len(padding)) + 1
774 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200775
Klement Sekeradab231a2016-12-21 08:50:14 +0100776 @classmethod
777 def reset_packet_infos(cls):
778 """ Reset the list of packet info objects and packet counts to zero """
779 cls._packet_infos = {}
780 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200781
Klement Sekeradab231a2016-12-21 08:50:14 +0100782 @classmethod
783 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200784 """
785 Create packet info object containing the source and destination indexes
786 and add it to the testcase's packet info list
787
Klement Sekeradab231a2016-12-21 08:50:14 +0100788 :param VppInterface src_if: source interface
789 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200790
791 :returns: _PacketInfo object
792
793 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200794 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100795 info.index = len(cls._packet_infos)
796 info.src = src_if.sw_if_index
797 info.dst = dst_if.sw_if_index
798 if isinstance(dst_if, VppSubInterface):
799 dst_idx = dst_if.parent.sw_if_index
800 else:
801 dst_idx = dst_if.sw_if_index
802 if dst_idx in cls._packet_count_for_dst_if_idx:
803 cls._packet_count_for_dst_if_idx[dst_idx] += 1
804 else:
805 cls._packet_count_for_dst_if_idx[dst_idx] = 1
806 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200807 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200808
Damjan Marionf56b77a2016-10-03 19:44:57 +0200809 @staticmethod
810 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200811 """
812 Convert _PacketInfo object to packet payload
813
814 :param info: _PacketInfo object
815
816 :returns: string containing serialized data from packet info
817 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100818 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
819 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200820
Damjan Marionf56b77a2016-10-03 19:44:57 +0200821 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800822 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200823 """
824 Convert packet payload to _PacketInfo object
825
826 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700827 :type payload: <class 'scapy.packet.Raw'>
828 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800829 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700830 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200831 :returns: _PacketInfo object containing de-serialized data from payload
832
833 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800834 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200835 info = _PacketInfo()
836 info.index = int(numbers[0])
837 info.src = int(numbers[1])
838 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100839 info.ip = int(numbers[3])
840 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200841 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200842
Damjan Marionf56b77a2016-10-03 19:44:57 +0200843 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200844 """
845 Iterate over the packet info list stored in the testcase
846 Start iteration with first element if info is None
847 Continue based on index in info if info is specified
848
849 :param info: info or None
850 :returns: next info in list or None if no more infos
851 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200852 if info is None:
853 next_index = 0
854 else:
855 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100856 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200857 return None
858 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100859 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200860
Klement Sekeraf62ae122016-10-11 11:47:09 +0200861 def get_next_packet_info_for_interface(self, src_index, info):
862 """
863 Search the packet info list for the next packet info with same source
864 interface index
865
866 :param src_index: source interface index to search for
867 :param info: packet info - where to start the search
868 :returns: packet info or None
869
870 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200871 while True:
872 info = self.get_next_packet_info(info)
873 if info is None:
874 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200875 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200876 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200877
Klement Sekeraf62ae122016-10-11 11:47:09 +0200878 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
879 """
880 Search the packet info list for the next packet info with same source
881 and destination interface indexes
882
883 :param src_index: source interface index to search for
884 :param dst_index: destination interface index to search for
885 :param info: packet info - where to start the search
886 :returns: packet info or None
887
888 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200889 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200890 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200891 if info is None:
892 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200893 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200894 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200895
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200896 def assert_equal(self, real_value, expected_value, name_or_class=None):
897 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100898 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200899 return
900 try:
901 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
902 msg = msg % (getdoc(name_or_class).strip(),
903 real_value, str(name_or_class(real_value)),
904 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100905 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200906 msg = "Invalid %s: %s does not match expected value %s" % (
907 name_or_class, real_value, expected_value)
908
909 self.assertEqual(real_value, expected_value, msg)
910
Klement Sekerab17dd962017-01-09 07:43:48 +0100911 def assert_in_range(self,
912 real_value,
913 expected_min,
914 expected_max,
915 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200916 if name is None:
917 msg = None
918 else:
919 msg = "Invalid %s: %s out of range <%s,%s>" % (
920 name, real_value, expected_min, expected_max)
921 self.assertTrue(expected_min <= real_value <= expected_max, msg)
922
Klement Sekerad81ae412018-05-16 10:52:54 +0200923 def assert_packet_checksums_valid(self, packet,
924 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700925 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200926 self.logger.debug(
927 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200928 udp_layers = ['UDP', 'UDPerror']
929 checksum_fields = ['cksum', 'chksum']
930 checksums = []
931 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700932 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200933 while True:
934 layer = temp.getlayer(counter)
935 if layer:
936 for cf in checksum_fields:
937 if hasattr(layer, cf):
938 if ignore_zero_udp_checksums and \
Klement Sekera6aa58b72019-05-16 14:34:55 +0200939 0 == getattr(layer, cf) and \
940 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200941 continue
942 delattr(layer, cf)
943 checksums.append((counter, cf))
944 else:
945 break
946 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200947 if 0 == len(checksums):
948 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700949 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200950 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200951 calc_sum = getattr(temp[layer], cf)
952 self.assert_equal(
953 getattr(received[layer], cf), calc_sum,
954 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
955 self.logger.debug(
956 "Checksum field `%s` on `%s` layer has correct value `%s`" %
957 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200958
959 def assert_checksum_valid(self, received_packet, layer,
960 field_name='chksum',
961 ignore_zero_checksum=False):
962 """ Check checksum of received packet on given layer """
963 received_packet_checksum = getattr(received_packet[layer], field_name)
964 if ignore_zero_checksum and 0 == received_packet_checksum:
965 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700966 recalculated = received_packet.__class__(
967 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200968 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700969 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +0200970 self.assert_equal(received_packet_checksum,
971 getattr(recalculated[layer], field_name),
972 "packet checksum on layer: %s" % layer)
973
974 def assert_ip_checksum_valid(self, received_packet,
975 ignore_zero_checksum=False):
976 self.assert_checksum_valid(received_packet, 'IP',
977 ignore_zero_checksum=ignore_zero_checksum)
978
979 def assert_tcp_checksum_valid(self, received_packet,
980 ignore_zero_checksum=False):
981 self.assert_checksum_valid(received_packet, 'TCP',
982 ignore_zero_checksum=ignore_zero_checksum)
983
984 def assert_udp_checksum_valid(self, received_packet,
985 ignore_zero_checksum=True):
986 self.assert_checksum_valid(received_packet, 'UDP',
987 ignore_zero_checksum=ignore_zero_checksum)
988
989 def assert_embedded_icmp_checksum_valid(self, received_packet):
990 if received_packet.haslayer(IPerror):
991 self.assert_checksum_valid(received_packet, 'IPerror')
992 if received_packet.haslayer(TCPerror):
993 self.assert_checksum_valid(received_packet, 'TCPerror')
994 if received_packet.haslayer(UDPerror):
995 self.assert_checksum_valid(received_packet, 'UDPerror',
996 ignore_zero_checksum=True)
997 if received_packet.haslayer(ICMPerror):
998 self.assert_checksum_valid(received_packet, 'ICMPerror')
999
1000 def assert_icmp_checksum_valid(self, received_packet):
1001 self.assert_checksum_valid(received_packet, 'ICMP')
1002 self.assert_embedded_icmp_checksum_valid(received_packet)
1003
1004 def assert_icmpv6_checksum_valid(self, pkt):
1005 if pkt.haslayer(ICMPv6DestUnreach):
1006 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1007 self.assert_embedded_icmp_checksum_valid(pkt)
1008 if pkt.haslayer(ICMPv6EchoRequest):
1009 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1010 if pkt.haslayer(ICMPv6EchoReply):
1011 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1012
Klement Sekera3a343d42019-05-16 14:35:46 +02001013 def get_packet_counter(self, counter):
1014 if counter.startswith("/"):
1015 counter_value = self.statistics.get_counter(counter)
1016 else:
1017 counters = self.vapi.cli("sh errors").split('\n')
Klement Sekera6aa58b72019-05-16 14:34:55 +02001018 counter_value = 0
Klement Sekera3a343d42019-05-16 14:35:46 +02001019 for i in range(1, len(counters) - 1):
1020 results = counters[i].split()
1021 if results[1] == counter:
1022 counter_value = int(results[0])
1023 break
1024 return counter_value
1025
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001026 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera6aa58b72019-05-16 14:34:55 +02001027 counter_value = self.get_packet_counter(counter)
1028 self.assert_equal(counter_value, expected_value,
1029 "packet counter `%s'" % counter)
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001030
Ole Troan233e4682019-05-16 15:01:34 +02001031 def assert_error_counter_equal(self, counter, expected_value):
1032 counter_value = self.statistics.get_err_counter(counter)
1033 self.assert_equal(counter_value, expected_value,
1034 "error counter `%s'" % counter)
1035
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001036 @classmethod
1037 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001038
1039 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1040 # * by Guido, only the main thread can be interrupted.
1041 # */
1042 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1043 if timeout == 0:
1044 # yield quantum
1045 if hasattr(os, 'sched_yield'):
1046 os.sched_yield()
1047 else:
1048 time.sleep(0)
1049 return
1050
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001051 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001052 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001053 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001054 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001055 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001056 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001057 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001058 "slept for %es instead of ~%es!",
1059 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001060 if hasattr(cls, 'logger'):
1061 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001062 "Finished sleep (%s) - slept %es (wanted %es)",
1063 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001064
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001065 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001066 self.vapi.cli("clear trace")
1067 intf.add_stream(pkts)
1068 self.pg_enable_capture(self.pg_interfaces)
1069 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001070
1071 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1072 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001073 if not timeout:
1074 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001075 for i in self.pg_interfaces:
1076 i.get_capture(0, timeout=timeout)
1077 i.assert_nothing_captured(remark=remark)
1078 timeout = 0.1
1079
Neale Rannsd7603d92019-03-28 08:56:10 +00001080 def send_and_expect(self, intf, pkts, output, n_rx=None):
1081 if not n_rx:
1082 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001083 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001084 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001085 return rx
1086
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001087 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1088 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001089 rx = output.get_capture(len(pkts))
1090 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001091 if not timeout:
1092 timeout = 1
1093 for i in self.pg_interfaces:
1094 if i not in outputs:
1095 i.get_capture(0, timeout=timeout)
1096 i.assert_nothing_captured()
1097 timeout = 0.1
1098
Neale Ranns52fae862018-01-08 04:41:42 -08001099 return rx
1100
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001101 def runTest(self):
1102 """ unittest calls runTest when TestCase is instantiated without a
1103 test case. Use case: Writing unittests against VppTestCase"""
1104 pass
1105
Damjan Marionf56b77a2016-10-03 19:44:57 +02001106
juraj.linkes184870a2018-07-16 14:22:01 +02001107def get_testcase_doc_name(test):
1108 return getdoc(test.__class__).splitlines()[0]
1109
1110
Ole Trøan5ba91592018-11-22 10:01:09 +00001111def get_test_description(descriptions, test):
1112 short_description = test.shortDescription()
1113 if descriptions and short_description:
1114 return short_description
1115 else:
1116 return str(test)
1117
1118
juraj.linkes40dd73b2018-09-21 13:55:16 +02001119class TestCaseInfo(object):
1120 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1121 self.logger = logger
1122 self.tempdir = tempdir
1123 self.vpp_pid = vpp_pid
1124 self.vpp_bin_path = vpp_bin_path
1125 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001126
1127
Damjan Marionf56b77a2016-10-03 19:44:57 +02001128class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001129 """
1130 @property result_string
1131 String variable to store the test case result string.
1132 @property errors
1133 List variable containing 2-tuples of TestCase instances and strings
1134 holding formatted tracebacks. Each tuple represents a test which
1135 raised an unexpected exception.
1136 @property failures
1137 List variable containing 2-tuples of TestCase instances and strings
1138 holding formatted tracebacks. Each tuple represents a test where
1139 a failure was explicitly signalled using the TestCase.assert*()
1140 methods.
1141 """
1142
juraj.linkes40dd73b2018-09-21 13:55:16 +02001143 failed_test_cases_info = set()
1144 core_crash_test_cases_info = set()
1145 current_test_case_info = None
1146
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001147 def __init__(self, stream=None, descriptions=None, verbosity=None,
1148 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001149 """
Klement Sekerada505f62017-01-04 12:58:53 +01001150 :param stream File descriptor to store where to report test results.
1151 Set to the standard error stream by default.
1152 :param descriptions Boolean variable to store information if to use
1153 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001154 :param verbosity Integer variable to store required verbosity level.
1155 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001156 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001157 self.stream = stream
1158 self.descriptions = descriptions
1159 self.verbosity = verbosity
1160 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001161 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001162
Damjan Marionf56b77a2016-10-03 19:44:57 +02001163 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001164 """
1165 Record a test succeeded result
1166
1167 :param test:
1168
1169 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001170 if self.current_test_case_info:
1171 self.current_test_case_info.logger.debug(
1172 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1173 test._testMethodName,
1174 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001175 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001176 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001177
juraj.linkescae64f82018-09-19 15:01:47 +02001178 self.send_result_through_pipe(test, PASS)
1179
Klement Sekeraf62ae122016-10-11 11:47:09 +02001180 def addSkip(self, test, reason):
1181 """
1182 Record a test skipped.
1183
1184 :param test:
1185 :param reason:
1186
1187 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001188 if self.current_test_case_info:
1189 self.current_test_case_info.logger.debug(
1190 "--- addSkip() %s.%s(%s) called, reason is %s" %
1191 (test.__class__.__name__, test._testMethodName,
1192 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001193 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001194 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001195
juraj.linkescae64f82018-09-19 15:01:47 +02001196 self.send_result_through_pipe(test, SKIP)
1197
juraj.linkes40dd73b2018-09-21 13:55:16 +02001198 def symlink_failed(self):
1199 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001200 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001201 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001202 link_path = os.path.join(
1203 failed_dir,
1204 '%s-FAILED' %
1205 os.path.basename(self.current_test_case_info.tempdir))
1206 if self.current_test_case_info.logger:
1207 self.current_test_case_info.logger.debug(
1208 "creating a link to the failed test")
1209 self.current_test_case_info.logger.debug(
1210 "os.symlink(%s, %s)" %
1211 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001212 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001213 if self.current_test_case_info.logger:
1214 self.current_test_case_info.logger.debug(
1215 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001216 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001217 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001218
Klement Sekeraf413bef2017-08-15 07:09:02 +02001219 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001220 if self.current_test_case_info.logger:
1221 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001222
juraj.linkescae64f82018-09-19 15:01:47 +02001223 def send_result_through_pipe(self, test, result):
1224 if hasattr(self, 'test_framework_result_pipe'):
1225 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001226 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001227 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001228
juraj.linkes40dd73b2018-09-21 13:55:16 +02001229 def log_error(self, test, err, fn_name):
1230 if self.current_test_case_info:
1231 if isinstance(test, unittest.suite._ErrorHolder):
1232 test_name = test.description
1233 else:
1234 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1235 test._testMethodName,
1236 test._testMethodDoc)
1237 self.current_test_case_info.logger.debug(
1238 "--- %s() %s called, err is %s" %
1239 (fn_name, test_name, err))
1240 self.current_test_case_info.logger.debug(
1241 "formatted exception is:\n%s" %
1242 "".join(format_exception(*err)))
1243
1244 def add_error(self, test, err, unittest_fn, error_type):
1245 if error_type == FAIL:
1246 self.log_error(test, err, 'addFailure')
1247 error_type_str = colorize("FAIL", RED)
1248 elif error_type == ERROR:
1249 self.log_error(test, err, 'addError')
1250 error_type_str = colorize("ERROR", RED)
1251 else:
1252 raise Exception('Error type %s cannot be used to record an '
1253 'error or a failure' % error_type)
1254
1255 unittest_fn(self, test, err)
1256 if self.current_test_case_info:
1257 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1258 (error_type_str,
1259 self.current_test_case_info.tempdir)
1260 self.symlink_failed()
1261 self.failed_test_cases_info.add(self.current_test_case_info)
1262 if is_core_present(self.current_test_case_info.tempdir):
1263 if not self.current_test_case_info.core_crash_test:
1264 if isinstance(test, unittest.suite._ErrorHolder):
1265 test_name = str(test)
1266 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001267 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001268 get_testcase_doc_name(test), test.id())
1269 self.current_test_case_info.core_crash_test = test_name
1270 self.core_crash_test_cases_info.add(
1271 self.current_test_case_info)
1272 else:
1273 self.result_string = '%s [no temp dir]' % error_type_str
1274
1275 self.send_result_through_pipe(test, error_type)
1276
Damjan Marionf56b77a2016-10-03 19:44:57 +02001277 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001278 """
1279 Record a test failed result
1280
1281 :param test:
1282 :param err: error message
1283
1284 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001285 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001286
Damjan Marionf56b77a2016-10-03 19:44:57 +02001287 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001288 """
1289 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001290
Klement Sekeraf62ae122016-10-11 11:47:09 +02001291 :param test:
1292 :param err: error message
1293
1294 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001295 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001296
Damjan Marionf56b77a2016-10-03 19:44:57 +02001297 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001298 """
1299 Get test description
1300
1301 :param test:
1302 :returns: test description
1303
1304 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001305 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001306
Damjan Marionf56b77a2016-10-03 19:44:57 +02001307 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001308 """
1309 Start a test
1310
1311 :param test:
1312
1313 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001314
1315 def print_header(test):
1316 if not hasattr(test.__class__, '_header_printed'):
1317 print(double_line_delim)
1318 print(colorize(getdoc(test).splitlines()[0], GREEN))
1319 print(double_line_delim)
1320 test.__class__._header_printed = True
1321
1322 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001323
Damjan Marionf56b77a2016-10-03 19:44:57 +02001324 unittest.TestResult.startTest(self, test)
1325 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001326 self.stream.writeln(
1327 "Starting " + self.getDescription(test) + " ...")
1328 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001329
Damjan Marionf56b77a2016-10-03 19:44:57 +02001330 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001331 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001332 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001333
1334 :param test:
1335
1336 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001337 unittest.TestResult.stopTest(self, test)
1338 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001339 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001340 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001341 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001342 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001343 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001344 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001345 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001346
1347 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001348
Damjan Marionf56b77a2016-10-03 19:44:57 +02001349 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001350 """
1351 Print errors from running the test case
1352 """
juraj.linkesabec0122018-11-16 17:28:56 +01001353 if len(self.errors) > 0 or len(self.failures) > 0:
1354 self.stream.writeln()
1355 self.printErrorList('ERROR', self.errors)
1356 self.printErrorList('FAIL', self.failures)
1357
1358 # ^^ that is the last output from unittest before summary
1359 if not self.runner.print_summary:
1360 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1361 self.stream = devnull
1362 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001363
Damjan Marionf56b77a2016-10-03 19:44:57 +02001364 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001365 """
1366 Print error list to the output stream together with error type
1367 and test case description.
1368
1369 :param flavour: error type
1370 :param errors: iterable errors
1371
1372 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001373 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001374 self.stream.writeln(double_line_delim)
1375 self.stream.writeln("%s: %s" %
1376 (flavour, self.getDescription(test)))
1377 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001378 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001379
1380
Damjan Marionf56b77a2016-10-03 19:44:57 +02001381class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001382 """
Klement Sekera104543f2017-02-03 07:29:43 +01001383 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001384 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001385
Klement Sekeraf62ae122016-10-11 11:47:09 +02001386 @property
1387 def resultclass(self):
1388 """Class maintaining the results of the tests"""
1389 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001390
juraj.linkes184870a2018-07-16 14:22:01 +02001391 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001392 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001393 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001394 # ignore stream setting here, use hard-coded stdout to be in sync
1395 # with prints from VppTestCase methods ...
1396 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1397 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001398 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001399 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001400
juraj.linkesabec0122018-11-16 17:28:56 +01001401 self.orig_stream = self.stream
1402 self.resultclass.test_framework_result_pipe = result_pipe
1403
1404 self.print_summary = print_summary
1405
1406 def _makeResult(self):
1407 return self.resultclass(self.stream,
1408 self.descriptions,
1409 self.verbosity,
1410 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001411
Damjan Marionf56b77a2016-10-03 19:44:57 +02001412 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001413 """
1414 Run the tests
1415
1416 :param test:
1417
1418 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001419 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001420
1421 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001422 if not self.print_summary:
1423 self.stream = self.orig_stream
1424 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001425 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001426
1427
1428class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001429 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001430 self.logger = logger
1431 self.args = args
1432 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001433 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001434 super(Worker, self).__init__()
1435
1436 def run(self):
1437 executable = self.args[0]
1438 self.logger.debug("Running executable w/args `%s'" % self.args)
1439 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001440 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001441 env["CK_LOG_FILE_NAME"] = "-"
1442 self.process = subprocess.Popen(
1443 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1444 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1445 out, err = self.process.communicate()
1446 self.logger.debug("Finished running `%s'" % executable)
1447 self.logger.info("Return code is `%s'" % self.process.returncode)
1448 self.logger.info(single_line_delim)
1449 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1450 self.logger.info(single_line_delim)
1451 self.logger.info(out)
1452 self.logger.info(single_line_delim)
1453 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1454 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001455 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001456 self.logger.info(single_line_delim)
1457 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001458
Klement Sekera6aa58b72019-05-16 14:34:55 +02001459
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001460if __name__ == '__main__':
1461 pass