blob: 47de2c4d967109ea78c65d0e0b406c89add5f1ae [file] [log] [blame]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001#!/usr/bin/env python
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01003from __future__ import print_function
4import gc
Paul Vinciguerra72f00042018-11-25 11:05:13 -08005import sys
Ole Trøan162989e2018-11-26 10:27:50 +00006import os
7import select
8import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +02009import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020010import time
Paul Vinciguerra72f00042018-11-25 11:05:13 -080011import faulthandler
Ole Trøan162989e2018-11-26 10:27:50 +000012import random
13import copy
Paul Vinciguerra72f00042018-11-25 11:05:13 -080014import psutil
juraj.linkes68ebc832018-11-29 09:37:08 +010015import platform
Ole Trøan162989e2018-11-26 10:27:50 +000016from collections import deque
17from threading import Thread, Event
18from inspect import getdoc, isclass
19from traceback import format_exception
20from logging import FileHandler, DEBUG, Formatter
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -070021
22import scapy.compat
Ole Trøan162989e2018-11-26 10:27:50 +000023from scapy.packet import Raw
24from hook import StepHook, PollHook, VppDiedError
Paul Vinciguerra919efad2018-12-17 21:43:43 -080025from vpp_pg_interface import VppPGInterface
Ole Troana45dc072018-12-21 16:04:22 +010026from vpp_sub_interface import VppSubInterface
Ole Trøan162989e2018-11-26 10:27:50 +000027from vpp_lo_interface import VppLoInterface
Neale Ranns192b13f2019-03-15 02:16:20 -070028from vpp_bvi_interface import VppBviInterface
Ole Trøan162989e2018-11-26 10:27:50 +000029from vpp_papi_provider import VppPapiProvider
30from vpp_papi.vpp_stats import VPPStats
31from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
32 get_logger, colorize
33from vpp_object import VppObjectRegistry
34from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020035from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
36from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
37from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080038
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010039if os.name == 'posix' and sys.version_info[0] < 3:
40 # using subprocess32 is recommended by python official documentation
41 # @ https://docs.python.org/2/library/subprocess.html
42 import subprocess32 as subprocess
43else:
44 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020045
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080046# Python2/3 compatible
47try:
48 input = raw_input
49except NameError:
50 pass
51
juraj.linkescae64f82018-09-19 15:01:47 +020052PASS = 0
53FAIL = 1
54ERROR = 2
55SKIP = 3
56TEST_RUN = 4
57
Klement Sekeraebbaf552018-02-17 13:41:33 +010058debug_framework = False
59if os.getenv('TEST_DEBUG', "0") == "1":
60 debug_framework = True
61 import debug_internal
62
Klement Sekeraf62ae122016-10-11 11:47:09 +020063"""
64 Test framework module.
65
66 The module provides a set of tools for constructing and running tests and
67 representing the results.
68"""
69
Klement Sekeraf62ae122016-10-11 11:47:09 +020070
Damjan Marionf56b77a2016-10-03 19:44:57 +020071class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020072 """Private class to create packet info object.
73
74 Help process information about the next packet.
75 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020076 """
Matej Klotton86d87c42016-11-11 11:38:55 +010077 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020078 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010079 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020080 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010081 #: Store the index of the destination packet generator interface
82 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020083 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010084 #: Store expected ip version
85 ip = -1
86 #: Store expected upper protocol
87 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010088 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020089 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020090
Matej Klotton16a14cd2016-12-07 15:09:13 +010091 def __eq__(self, other):
92 index = self.index == other.index
93 src = self.src == other.src
94 dst = self.dst == other.dst
95 data = self.data == other.data
96 return index and src and dst and data
97
Klement Sekeraf62ae122016-10-11 11:47:09 +020098
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010099def pump_output(testclass):
100 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100101 stdout_fragment = ""
102 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400103 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100104 readable = select.select([testclass.vpp.stdout.fileno(),
105 testclass.vpp.stderr.fileno(),
106 testclass.pump_thread_wakeup_pipe[0]],
107 [], [])[0]
108 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100109 read = os.read(testclass.vpp.stdout.fileno(), 102400)
110 if len(read) > 0:
111 split = read.splitlines(True)
112 if len(stdout_fragment) > 0:
113 split[0] = "%s%s" % (stdout_fragment, split[0])
114 if len(split) > 0 and split[-1].endswith("\n"):
115 limit = None
116 else:
117 limit = -1
118 stdout_fragment = split[-1]
119 testclass.vpp_stdout_deque.extend(split[:limit])
120 if not testclass.cache_vpp_output:
121 for line in split[:limit]:
122 testclass.logger.debug(
123 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100124 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100125 read = os.read(testclass.vpp.stderr.fileno(), 102400)
126 if len(read) > 0:
127 split = read.splitlines(True)
128 if len(stderr_fragment) > 0:
129 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan7f991832018-12-06 17:35:12 +0100130 if len(split) > 0 and split[-1].endswith(b"\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100131 limit = None
132 else:
133 limit = -1
134 stderr_fragment = split[-1]
135 testclass.vpp_stderr_deque.extend(split[:limit])
136 if not testclass.cache_vpp_output:
137 for line in split[:limit]:
138 testclass.logger.debug(
139 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800140 # ignoring the dummy pipe here intentionally - the
141 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200142
143
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800144def _is_skip_aarch64_set():
juraj.linkes68ebc832018-11-29 09:37:08 +0100145 return os.getenv('SKIP_AARCH64', 'n').lower() in ('yes', 'y', '1')
146
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800147is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100148
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800149
150def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100151 return platform.machine() == 'aarch64'
152
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800153is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100154
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800155
156def _running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100157 s = os.getenv("EXTENDED_TESTS", "n")
158 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100159
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800160running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100161
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800162
163def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100164 os_id = os.getenv("OS_ID", "")
165 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200166
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800167running_on_centos = _running_on_centos
168
Klement Sekerad3e671e2017-09-29 12:36:37 +0200169
Klement Sekera909a6a12017-08-08 04:33:53 +0200170class KeepAliveReporter(object):
171 """
172 Singleton object which reports test start to parent process
173 """
174 _shared_state = {}
175
176 def __init__(self):
177 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800178 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200179
180 @property
181 def pipe(self):
182 return self._pipe
183
184 @pipe.setter
185 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800186 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200187 raise Exception("Internal error - pipe should only be set once.")
188 self._pipe = pipe
189
juraj.linkes40dd73b2018-09-21 13:55:16 +0200190 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200191 """
192 Write current test tmpdir & desc to keep-alive pipe to signal liveness
193 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200194 if self.pipe is None:
195 # if not running forked..
196 return
197
Klement Sekera909a6a12017-08-08 04:33:53 +0200198 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200199 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200200 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200201 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200202
Dave Wallacee2efd122017-09-30 22:04:21 -0400203 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200204
205
Damjan Marionf56b77a2016-10-03 19:44:57 +0200206class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100207 """This subclass is a base class for VPP test cases that are implemented as
208 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200209 """
210
Ole Troana45dc072018-12-21 16:04:22 +0100211 extra_vpp_punt_config = []
212 extra_vpp_plugin_config = []
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100213
Klement Sekeraf62ae122016-10-11 11:47:09 +0200214 @property
215 def packet_infos(self):
216 """List of packet infos"""
217 return self._packet_infos
218
Klement Sekeradab231a2016-12-21 08:50:14 +0100219 @classmethod
220 def get_packet_count_for_if_idx(cls, dst_if_index):
221 """Get the number of packet info for specified destination if index"""
222 if dst_if_index in cls._packet_count_for_dst_if_idx:
223 return cls._packet_count_for_dst_if_idx[dst_if_index]
224 else:
225 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200226
227 @classmethod
228 def instance(cls):
229 """Return the instance of this testcase"""
230 return cls.test_instance
231
Damjan Marionf56b77a2016-10-03 19:44:57 +0200232 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200233 def set_debug_flags(cls, d):
234 cls.debug_core = False
235 cls.debug_gdb = False
236 cls.debug_gdbserver = False
237 if d is None:
238 return
239 dl = d.lower()
240 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200241 cls.debug_core = True
242 elif dl == "gdb":
243 cls.debug_gdb = True
244 elif dl == "gdbserver":
245 cls.debug_gdbserver = True
246 else:
247 raise Exception("Unrecognized DEBUG option: '%s'" % d)
248
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800249 @staticmethod
250 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200251 cpu_usage_list = [set(range(psutil.cpu_count()))]
252 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
253 if 'vpp_main' == p.info['name']]
254 for vpp_process in vpp_processes:
255 for cpu_usage_set in cpu_usage_list:
256 try:
257 cpu_num = vpp_process.cpu_num()
258 if cpu_num in cpu_usage_set:
259 cpu_usage_set_index = cpu_usage_list.index(
260 cpu_usage_set)
261 if cpu_usage_set_index == len(cpu_usage_list) - 1:
262 cpu_usage_list.append({cpu_num})
263 else:
264 cpu_usage_list[cpu_usage_set_index + 1].add(
265 cpu_num)
266 cpu_usage_set.remove(cpu_num)
267 break
268 except psutil.NoSuchProcess:
269 pass
270
271 for cpu_usage_set in cpu_usage_list:
272 if len(cpu_usage_set) > 0:
273 min_usage_set = cpu_usage_set
274 break
275
276 return random.choice(tuple(min_usage_set))
277
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800278 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200279 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200280 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100281 s = os.getenv("STEP", "n")
282 cls.step = True if s.lower() in ("y", "yes", "1") else False
283 d = os.getenv("DEBUG", None)
284 c = os.getenv("CACHE_OUTPUT", "1")
285 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200286 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100287 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
288 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Dave Barach7d31ab22019-05-08 19:18:18 -0400289 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100290 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
291 plugin_path = None
292 if cls.plugin_path is not None:
293 if cls.extern_plugin_path is not None:
294 plugin_path = "%s:%s" % (
295 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100296 else:
297 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100298 elif cls.extern_plugin_path is not None:
299 plugin_path = cls.extern_plugin_path
Ole Troana45dc072018-12-21 16:04:22 +0100300 debug_cli = ""
Klement Sekera01bbbe92016-11-02 09:25:05 +0100301 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Ole Troana45dc072018-12-21 16:04:22 +0100302 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100303 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100304 size = os.getenv("COREDUMP_SIZE")
Ole Troana45dc072018-12-21 16:04:22 +0100305 if size is not None:
306 coredump_size = "coredump-size %s" % size
307 if coredump_size is None:
308 coredump_size = "coredump-size unlimited"
juraj.linkes184870a2018-07-16 14:22:01 +0200309
Ole Troana45dc072018-12-21 16:04:22 +0100310 cpu_core_number = cls.get_least_used_cpu()
juraj.linkes184870a2018-07-16 14:22:01 +0200311
Ole Troana45dc072018-12-21 16:04:22 +0100312 cls.vpp_cmdline = [cls.vpp_bin, "unix",
313 "{", "nodaemon", debug_cli, "full-coredump",
314 coredump_size, "runtime-dir", cls.tempdir, "}",
315 "api-trace", "{", "on", "}", "api-segment", "{",
316 "prefix", cls.shm_prefix, "}", "cpu", "{",
Ole Troan4ff09ae2019-04-15 11:27:22 +0200317 "main-core", str(cpu_core_number), "}",
318 "statseg", "{", "socket-name", cls.stats_sock, "}",
319 "socksvr", "{", "socket-name", cls.api_sock, "}",
320 "plugins",
Ole Troana45dc072018-12-21 16:04:22 +0100321 "{", "plugin", "dpdk_plugin.so", "{", "disable",
Ole Troan2e1c8962019-04-10 09:44:23 +0200322 "}", "plugin", "rdma_plugin.so", "{", "disable",
Ole Troana45dc072018-12-21 16:04:22 +0100323 "}", "plugin", "unittest_plugin.so", "{", "enable",
324 "}"] + cls.extra_vpp_plugin_config + ["}", ]
325 if cls.extra_vpp_punt_config is not None:
326 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
Klement Sekera47e275b2017-03-21 08:21:25 +0100327 if plugin_path is not None:
Ole Troana45dc072018-12-21 16:04:22 +0100328 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
Dave Barach7d31ab22019-05-08 19:18:18 -0400329 if cls.test_plugin_path is not None:
330 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
331
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100332 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
333 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200334
335 @classmethod
336 def wait_for_enter(cls):
337 if cls.debug_gdbserver:
338 print(double_line_delim)
339 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
340 elif cls.debug_gdb:
341 print(double_line_delim)
342 print("Spawned VPP with PID: %d" % cls.vpp.pid)
343 else:
344 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
345 return
346 print(single_line_delim)
347 print("You can debug the VPP using e.g.:")
348 if cls.debug_gdbserver:
349 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
350 print("Now is the time to attach a gdb by running the above "
351 "command, set up breakpoints etc. and then resume VPP from "
352 "within gdb by issuing the 'continue' command")
353 elif cls.debug_gdb:
354 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
355 print("Now is the time to attach a gdb by running the above "
356 "command and set up breakpoints etc.")
357 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800358 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200359
360 @classmethod
361 def run_vpp(cls):
362 cmdline = cls.vpp_cmdline
363
364 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100365 gdbserver = '/usr/bin/gdbserver'
366 if not os.path.isfile(gdbserver) or \
367 not os.access(gdbserver, os.X_OK):
368 raise Exception("gdbserver binary '%s' does not exist or is "
369 "not executable" % gdbserver)
370
371 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200372 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
373
Klement Sekera931be3a2016-11-03 05:36:01 +0100374 try:
375 cls.vpp = subprocess.Popen(cmdline,
376 stdout=subprocess.PIPE,
377 stderr=subprocess.PIPE,
378 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800379 except subprocess.CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800380 cls.logger.critical("Subprocess returned with non-0 return code: ("
381 "%s)", e.returncode)
382 raise
383 except OSError as e:
384 cls.logger.critical("Subprocess returned with OS error: "
385 "(%s) %s", e.errno, e.strerror)
386 raise
387 except Exception as e:
388 cls.logger.exception("Subprocess returned unexpected from "
389 "%s:", cmdline)
Klement Sekera931be3a2016-11-03 05:36:01 +0100390 raise
391
Klement Sekera277b89c2016-10-28 13:20:27 +0200392 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100393
Damjan Marionf56b77a2016-10-03 19:44:57 +0200394 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200395 def wait_for_stats_socket(cls):
396 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800397 ok = False
398 while time.time() < deadline or \
399 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200400 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800401 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200402 break
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -0700403 cls.sleep(0.8)
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800404 if not ok:
405 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200406
407 @classmethod
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400408 def wait_for_coredump(cls):
409 corefile = cls.tempdir + "/core"
410 if os.path.isfile(corefile):
411 cls.logger.error("Waiting for coredump to complete: %s", corefile)
412 curr_size = os.path.getsize(corefile)
413 deadline = time.time() + 60
414 ok = False
415 while time.time() < deadline:
416 cls.sleep(1)
417 size = curr_size
418 curr_size = os.path.getsize(corefile)
419 if size == curr_size:
420 ok = True
421 break
422 if not ok:
423 cls.logger.error("Timed out waiting for coredump to complete:"
424 " %s", corefile)
425 else:
426 cls.logger.error("Coredump complete: %s, size %d",
427 corefile, curr_size)
428
429 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200430 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200431 """
432 Perform class setup before running the testcase
433 Remove shared memory files, start vpp and connect the vpp-api
434 """
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800435 super(VppTestCase, cls).setUpClass()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100436 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100437 random.seed()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100438 cls.logger = get_logger(cls.__name__)
439 if hasattr(cls, 'parallel_handler'):
440 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100441 cls.logger.propagate = False
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700442
Klement Sekeraf62ae122016-10-11 11:47:09 +0200443 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200444 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200445 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Ole Troan4ff09ae2019-04-15 11:27:22 +0200446 cls.api_sock = "%s/api.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200447 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
448 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100449 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
450 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200451 cls.file_handler.setLevel(DEBUG)
452 cls.logger.addHandler(cls.file_handler)
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700453 cls.logger.debug("--- setUpClass() for %s called ---" %
454 cls.__name__)
juraj.linkes184870a2018-07-16 14:22:01 +0200455 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200456 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200457 cls.logger.info("Temporary dir is %s, shm prefix is %s",
458 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200459 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100460 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100461 cls._captures = []
462 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200463 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100464 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100465 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200466 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200467 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200468 # need to catch exceptions here because if we raise, then the cleanup
469 # doesn't get called and we might end with a zombie vpp
470 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200471 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200472 cls.reporter.send_keep_alive(cls, 'setUpClass')
473 VppTestResult.current_test_case_info = TestCaseInfo(
474 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100475 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100476 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100477 cls.pump_thread_stop_flag = Event()
478 cls.pump_thread_wakeup_pipe = os.pipe()
479 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100480 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100481 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200482 if cls.debug_gdb or cls.debug_gdbserver:
483 read_timeout = 0
484 else:
485 read_timeout = 5
486 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
487 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100488 if cls.step:
489 hook = StepHook(cls)
490 else:
491 hook = PollHook(cls)
492 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200493 cls.wait_for_stats_socket()
494 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200495 try:
496 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100497 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200498 cls.vpp_startup_failed = True
499 cls.logger.critical(
500 "VPP died shortly after startup, check the"
501 " output to standard error for possible cause")
502 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100503 try:
504 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100505 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100506 try:
507 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100508 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100509 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100510 if cls.debug_gdbserver:
511 print(colorize("You're running VPP inside gdbserver but "
512 "VPP-API connection failed, did you forget "
513 "to 'continue' VPP from within gdb?", RED))
514 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100515 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100516 try:
517 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100518 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100519 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100520 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200521
Damjan Marionf56b77a2016-10-03 19:44:57 +0200522 @classmethod
523 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200524 """
525 Disconnect vpp-api, kill vpp and cleanup shared memory files
526 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200527 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
528 cls.vpp.poll()
529 if cls.vpp.returncode is None:
530 print(double_line_delim)
531 print("VPP or GDB server is still running")
532 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800533 input("When done debugging, press ENTER to kill the "
534 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200535
juraj.linkes184870a2018-07-16 14:22:01 +0200536 # first signal that we want to stop the pump thread, then wake it up
537 if hasattr(cls, 'pump_thread_stop_flag'):
538 cls.pump_thread_stop_flag.set()
539 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100540 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100541 if hasattr(cls, 'pump_thread'):
542 cls.logger.debug("Waiting for pump thread to stop")
543 cls.pump_thread.join()
544 if hasattr(cls, 'vpp_stderr_reader_thread'):
545 cls.logger.debug("Waiting for stdderr pump to stop")
546 cls.vpp_stderr_reader_thread.join()
547
Klement Sekeraf62ae122016-10-11 11:47:09 +0200548 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100549 if hasattr(cls, 'vapi'):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700550 cls.logger.debug("Disconnecting class vapi client on %s",
551 cls.__name__)
Klement Sekera0529a742016-12-02 07:05:24 +0100552 cls.vapi.disconnect()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700553 cls.logger.debug("Deleting class vapi attribute on %s",
554 cls.__name__)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100555 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200556 cls.vpp.poll()
557 if cls.vpp.returncode is None:
Dave Wallace3e9b7a22019-04-09 20:19:51 -0400558 cls.wait_for_coredump()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100559 cls.logger.debug("Sending TERM to vpp")
Dave Barachad646872019-05-06 10:49:41 -0400560 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100561 cls.logger.debug("Waiting for vpp to die")
562 cls.vpp.communicate()
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700563 cls.logger.debug("Deleting class vpp attribute on %s",
564 cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200565 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200566
Klement Sekera3747c752017-04-10 06:30:17 +0200567 if cls.vpp_startup_failed:
568 stdout_log = cls.logger.info
569 stderr_log = cls.logger.critical
570 else:
571 stdout_log = cls.logger.info
572 stderr_log = cls.logger.info
573
Klement Sekerae4504c62016-12-08 10:16:41 +0100574 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200575 stdout_log(single_line_delim)
576 stdout_log('VPP output to stdout while running %s:', cls.__name__)
577 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100578 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200579 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
580 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200581 stdout_log('\n%s', vpp_output)
582 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200583
Klement Sekerae4504c62016-12-08 10:16:41 +0100584 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200585 stderr_log(single_line_delim)
586 stderr_log('VPP output to stderr while running %s:', cls.__name__)
587 stderr_log(single_line_delim)
Klement Sekera0ede47a2019-01-29 11:49:09 +0100588 vpp_output = "".join(cls.vpp_stderr_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200589 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
590 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200591 stderr_log('\n%s', vpp_output)
592 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200593
Damjan Marionf56b77a2016-10-03 19:44:57 +0200594 @classmethod
595 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200596 """ Perform final cleanup after running all tests in this test-case """
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700597 cls.logger.debug("--- tearDownClass() for %s called ---" %
598 cls.__name__)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200599 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200600 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200601 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100602 cls.reset_packet_infos()
603 if debug_framework:
604 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200605
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700606 def show_commands_at_teardown(self):
607 """ Allow subclass specific teardown logging additions."""
608 self.logger.info("--- No test specific show commands provided. ---")
609
Damjan Marionf56b77a2016-10-03 19:44:57 +0200610 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200611 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100612 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
613 (self.__class__.__name__, self._testMethodName,
614 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200615 if not self.vpp_dead:
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700616 self.logger.info(
617 "--- Logging show commands common to all testcases. ---")
Klement Sekerad91fa612019-01-15 13:25:09 +0100618 self.logger.debug(self.vapi.cli("show trace max 1000"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700619 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200620 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200621 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200622 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800623 self.logger.info(self.vapi.ppcli("show log"))
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700624 self.logger.info("Logging testcase specific show commands.")
625 self.show_commands_at_teardown()
Klement Sekera10db26f2017-01-11 08:16:53 +0100626 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500627 # Save/Dump VPP api trace log
628 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
629 tmp_api_trace = "/tmp/%s" % api_trace
630 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
631 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
632 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
633 vpp_api_trace_log))
634 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500635 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500636 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100637 else:
638 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200639
Damjan Marionf56b77a2016-10-03 19:44:57 +0200640 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200641 """ Clear trace before running each test"""
Paul Vinciguerra8d991d92019-01-25 14:05:48 -0800642 super(VppTestCase, self).setUp()
Klement Sekera909a6a12017-08-08 04:33:53 +0200643 self.reporter.send_keep_alive(self)
Klement Sekera0c1519b2016-12-08 05:03:32 +0100644 if self.vpp_dead:
645 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100646 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100647 self.vpp_stdout_deque.append(
648 "--- test setUp() for %s.%s(%s) starts here ---\n" %
649 (self.__class__.__name__, self._testMethodName,
650 self._testMethodDoc))
651 self.vpp_stderr_deque.append(
652 "--- test setUp() for %s.%s(%s) starts here ---\n" %
653 (self.__class__.__name__, self._testMethodName,
654 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200655 self.vapi.cli("clear trace")
656 # store the test instance inside the test class - so that objects
657 # holding the class can access instance methods (like assertEqual)
658 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200659
Damjan Marionf56b77a2016-10-03 19:44:57 +0200660 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200661 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200662 """
663 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200664
Klement Sekera75e7d132017-09-20 08:26:30 +0200665 :param interfaces: iterable interface indexes (if None,
666 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200667
Klement Sekeraf62ae122016-10-11 11:47:09 +0200668 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200669 if interfaces is None:
670 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200671 for i in interfaces:
672 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200673
Damjan Marionf56b77a2016-10-03 19:44:57 +0200674 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100675 def register_capture(cls, cap_name):
676 """ Register a capture in the testclass """
677 # add to the list of captures with current timestamp
678 cls._captures.append((time.time(), cap_name))
679 # filter out from zombies
680 cls._zombie_captures = [(stamp, name)
681 for (stamp, name) in cls._zombie_captures
682 if name != cap_name]
683
684 @classmethod
685 def pg_start(cls):
686 """ Remove any zombie captures and enable the packet generator """
687 # how long before capture is allowed to be deleted - otherwise vpp
688 # crashes - 100ms seems enough (this shouldn't be needed at all)
689 capture_ttl = 0.1
690 now = time.time()
691 for stamp, cap_name in cls._zombie_captures:
692 wait = stamp + capture_ttl - now
693 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100694 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100695 now = time.time()
696 cls.logger.debug("Removing zombie capture %s" % cap_name)
697 cls.vapi.cli('packet-generator delete %s' % cap_name)
698
Klement Sekerad91fa612019-01-15 13:25:09 +0100699 cls.vapi.cli("trace add pg-input 1000")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200700 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100701 cls._zombie_captures = cls._captures
702 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200703
Damjan Marionf56b77a2016-10-03 19:44:57 +0200704 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200705 def create_pg_interfaces(cls, interfaces):
706 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100707 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200708
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100709 :param interfaces: iterable indexes of the interfaces.
710 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200711
Klement Sekeraf62ae122016-10-11 11:47:09 +0200712 """
713 result = []
714 for i in interfaces:
715 intf = VppPGInterface(cls, i)
716 setattr(cls, intf.name, intf)
717 result.append(intf)
718 cls.pg_interfaces = result
719 return result
720
Matej Klotton0178d522016-11-04 11:11:44 +0100721 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200722 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100723 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100724 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100725
Klement Sekerab9ef2732018-06-24 22:49:33 +0200726 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100727 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100728 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200729 result = [VppLoInterface(cls) for i in range(count)]
730 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100731 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100732 cls.lo_interfaces = result
733 return result
734
Neale Ranns192b13f2019-03-15 02:16:20 -0700735 @classmethod
736 def create_bvi_interfaces(cls, count):
737 """
738 Create BVI interfaces.
739
740 :param count: number of interfaces created.
741 :returns: List of created interfaces.
742 """
743 result = [VppBviInterface(cls) for i in range(count)]
744 for intf in result:
745 setattr(cls, intf.name, intf)
746 cls.bvi_interfaces = result
747 return result
748
Damjan Marionf56b77a2016-10-03 19:44:57 +0200749 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200750 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200751 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200752 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200753 NOTE: Currently works only when Raw layer is present.
754
755 :param packet: packet
756 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200757 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200758
759 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200760 packet_len = len(packet) + 4
761 extend = size - packet_len
762 if extend > 0:
Alexandre Poirriercc991492019-05-07 10:39:57 +0200763 num = (extend // len(padding)) + 1
764 packet[Raw].load += (padding * num)[:extend].encode("ascii")
Damjan Marionf56b77a2016-10-03 19:44:57 +0200765
Klement Sekeradab231a2016-12-21 08:50:14 +0100766 @classmethod
767 def reset_packet_infos(cls):
768 """ Reset the list of packet info objects and packet counts to zero """
769 cls._packet_infos = {}
770 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200771
Klement Sekeradab231a2016-12-21 08:50:14 +0100772 @classmethod
773 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200774 """
775 Create packet info object containing the source and destination indexes
776 and add it to the testcase's packet info list
777
Klement Sekeradab231a2016-12-21 08:50:14 +0100778 :param VppInterface src_if: source interface
779 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200780
781 :returns: _PacketInfo object
782
783 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200784 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100785 info.index = len(cls._packet_infos)
786 info.src = src_if.sw_if_index
787 info.dst = dst_if.sw_if_index
788 if isinstance(dst_if, VppSubInterface):
789 dst_idx = dst_if.parent.sw_if_index
790 else:
791 dst_idx = dst_if.sw_if_index
792 if dst_idx in cls._packet_count_for_dst_if_idx:
793 cls._packet_count_for_dst_if_idx[dst_idx] += 1
794 else:
795 cls._packet_count_for_dst_if_idx[dst_idx] = 1
796 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200797 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200798
Damjan Marionf56b77a2016-10-03 19:44:57 +0200799 @staticmethod
800 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200801 """
802 Convert _PacketInfo object to packet payload
803
804 :param info: _PacketInfo object
805
806 :returns: string containing serialized data from packet info
807 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100808 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
809 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200810
Damjan Marionf56b77a2016-10-03 19:44:57 +0200811 @staticmethod
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800812 def payload_to_info(payload, payload_field='load'):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200813 """
814 Convert packet payload to _PacketInfo object
815
816 :param payload: packet payload
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700817 :type payload: <class 'scapy.packet.Raw'>
818 :param payload_field: packet fieldname of payload "load" for
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800819 <class 'scapy.packet.Raw'>
Paul Vinciguerra22364e42019-03-12 20:04:56 -0700820 :type payload_field: str
Klement Sekeraf62ae122016-10-11 11:47:09 +0200821 :returns: _PacketInfo object containing de-serialized data from payload
822
823 """
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800824 numbers = getattr(payload, payload_field).split()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200825 info = _PacketInfo()
826 info.index = int(numbers[0])
827 info.src = int(numbers[1])
828 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100829 info.ip = int(numbers[3])
830 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200831 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200832
Damjan Marionf56b77a2016-10-03 19:44:57 +0200833 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200834 """
835 Iterate over the packet info list stored in the testcase
836 Start iteration with first element if info is None
837 Continue based on index in info if info is specified
838
839 :param info: info or None
840 :returns: next info in list or None if no more infos
841 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200842 if info is None:
843 next_index = 0
844 else:
845 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100846 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200847 return None
848 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100849 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200850
Klement Sekeraf62ae122016-10-11 11:47:09 +0200851 def get_next_packet_info_for_interface(self, src_index, info):
852 """
853 Search the packet info list for the next packet info with same source
854 interface index
855
856 :param src_index: source interface index to search for
857 :param info: packet info - where to start the search
858 :returns: packet info or None
859
860 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200861 while True:
862 info = self.get_next_packet_info(info)
863 if info is None:
864 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200865 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200866 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200867
Klement Sekeraf62ae122016-10-11 11:47:09 +0200868 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
869 """
870 Search the packet info list for the next packet info with same source
871 and destination interface indexes
872
873 :param src_index: source interface index to search for
874 :param dst_index: destination interface index to search for
875 :param info: packet info - where to start the search
876 :returns: packet info or None
877
878 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200879 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200880 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200881 if info is None:
882 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200883 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200884 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200885
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200886 def assert_equal(self, real_value, expected_value, name_or_class=None):
887 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100888 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200889 return
890 try:
891 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
892 msg = msg % (getdoc(name_or_class).strip(),
893 real_value, str(name_or_class(real_value)),
894 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100895 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200896 msg = "Invalid %s: %s does not match expected value %s" % (
897 name_or_class, real_value, expected_value)
898
899 self.assertEqual(real_value, expected_value, msg)
900
Klement Sekerab17dd962017-01-09 07:43:48 +0100901 def assert_in_range(self,
902 real_value,
903 expected_min,
904 expected_max,
905 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200906 if name is None:
907 msg = None
908 else:
909 msg = "Invalid %s: %s out of range <%s,%s>" % (
910 name, real_value, expected_min, expected_max)
911 self.assertTrue(expected_min <= real_value <= expected_max, msg)
912
Klement Sekerad81ae412018-05-16 10:52:54 +0200913 def assert_packet_checksums_valid(self, packet,
914 ignore_zero_udp_checksums=True):
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700915 received = packet.__class__(scapy.compat.raw(packet))
Klement Sekera31da2e32018-06-24 22:49:55 +0200916 self.logger.debug(
917 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200918 udp_layers = ['UDP', 'UDPerror']
919 checksum_fields = ['cksum', 'chksum']
920 checksums = []
921 counter = 0
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700922 temp = received.__class__(scapy.compat.raw(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200923 while True:
924 layer = temp.getlayer(counter)
925 if layer:
926 for cf in checksum_fields:
927 if hasattr(layer, cf):
928 if ignore_zero_udp_checksums and \
Ole Troana45dc072018-12-21 16:04:22 +0100929 0 == getattr(layer, cf) and \
930 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200931 continue
932 delattr(layer, cf)
933 checksums.append((counter, cf))
934 else:
935 break
936 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200937 if 0 == len(checksums):
938 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700939 temp = temp.__class__(scapy.compat.raw(temp))
Klement Sekerad81ae412018-05-16 10:52:54 +0200940 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200941 calc_sum = getattr(temp[layer], cf)
942 self.assert_equal(
943 getattr(received[layer], cf), calc_sum,
944 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
945 self.logger.debug(
946 "Checksum field `%s` on `%s` layer has correct value `%s`" %
947 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200948
949 def assert_checksum_valid(self, received_packet, layer,
950 field_name='chksum',
951 ignore_zero_checksum=False):
952 """ Check checksum of received packet on given layer """
953 received_packet_checksum = getattr(received_packet[layer], field_name)
954 if ignore_zero_checksum and 0 == received_packet_checksum:
955 return
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700956 recalculated = received_packet.__class__(
957 scapy.compat.raw(received_packet))
Klement Sekerad81ae412018-05-16 10:52:54 +0200958 delattr(recalculated[layer], field_name)
Paul Vinciguerraa7427ec2019-03-10 10:04:23 -0700959 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
Klement Sekerad81ae412018-05-16 10:52:54 +0200960 self.assert_equal(received_packet_checksum,
961 getattr(recalculated[layer], field_name),
962 "packet checksum on layer: %s" % layer)
963
964 def assert_ip_checksum_valid(self, received_packet,
965 ignore_zero_checksum=False):
966 self.assert_checksum_valid(received_packet, 'IP',
967 ignore_zero_checksum=ignore_zero_checksum)
968
969 def assert_tcp_checksum_valid(self, received_packet,
970 ignore_zero_checksum=False):
971 self.assert_checksum_valid(received_packet, 'TCP',
972 ignore_zero_checksum=ignore_zero_checksum)
973
974 def assert_udp_checksum_valid(self, received_packet,
975 ignore_zero_checksum=True):
976 self.assert_checksum_valid(received_packet, 'UDP',
977 ignore_zero_checksum=ignore_zero_checksum)
978
979 def assert_embedded_icmp_checksum_valid(self, received_packet):
980 if received_packet.haslayer(IPerror):
981 self.assert_checksum_valid(received_packet, 'IPerror')
982 if received_packet.haslayer(TCPerror):
983 self.assert_checksum_valid(received_packet, 'TCPerror')
984 if received_packet.haslayer(UDPerror):
985 self.assert_checksum_valid(received_packet, 'UDPerror',
986 ignore_zero_checksum=True)
987 if received_packet.haslayer(ICMPerror):
988 self.assert_checksum_valid(received_packet, 'ICMPerror')
989
990 def assert_icmp_checksum_valid(self, received_packet):
991 self.assert_checksum_valid(received_packet, 'ICMP')
992 self.assert_embedded_icmp_checksum_valid(received_packet)
993
994 def assert_icmpv6_checksum_valid(self, pkt):
995 if pkt.haslayer(ICMPv6DestUnreach):
996 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
997 self.assert_embedded_icmp_checksum_valid(pkt)
998 if pkt.haslayer(ICMPv6EchoRequest):
999 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1000 if pkt.haslayer(ICMPv6EchoReply):
1001 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1002
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001003 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +01001004 if counter.startswith("/"):
1005 counter_value = self.statistics.get_counter(counter)
1006 self.assert_equal(counter_value, expected_value,
1007 "packet counter `%s'" % counter)
1008 else:
1009 counters = self.vapi.cli("sh errors").split('\n')
1010 counter_value = -1
1011 for i in range(1, len(counters) - 1):
1012 results = counters[i].split()
1013 if results[1] == counter:
1014 counter_value = int(results[0])
1015 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +01001016
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001017 @classmethod
1018 def sleep(cls, timeout, remark=None):
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001019
1020 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1021 # * by Guido, only the main thread can be interrupted.
1022 # */
1023 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1024 if timeout == 0:
1025 # yield quantum
1026 if hasattr(os, 'sched_yield'):
1027 os.sched_yield()
1028 else:
1029 time.sleep(0)
1030 return
1031
Klement Sekeraacb9b8e2017-02-14 02:55:31 +01001032 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001033 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001034 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +01001035 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001036 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001037 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Paul Vinciguerra0f6602c2019-03-10 09:10:54 -07001038 cls.logger.error("unexpected self.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001039 "slept for %es instead of ~%es!",
1040 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +00001041 if hasattr(cls, 'logger'):
1042 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001043 "Finished sleep (%s) - slept %es (wanted %es)",
1044 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +01001045
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001046 def pg_send(self, intf, pkts):
Neale Ranns52fae862018-01-08 04:41:42 -08001047 self.vapi.cli("clear trace")
1048 intf.add_stream(pkts)
1049 self.pg_enable_capture(self.pg_interfaces)
1050 self.pg_start()
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001051
1052 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1053 self.pg_send(intf, pkts)
Neale Ranns947ea622018-06-07 23:48:20 -07001054 if not timeout:
1055 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -08001056 for i in self.pg_interfaces:
1057 i.get_capture(0, timeout=timeout)
1058 i.assert_nothing_captured(remark=remark)
1059 timeout = 0.1
1060
Neale Rannsd7603d92019-03-28 08:56:10 +00001061 def send_and_expect(self, intf, pkts, output, n_rx=None):
1062 if not n_rx:
1063 n_rx = len(pkts)
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001064 self.pg_send(intf, pkts)
Neale Rannsd7603d92019-03-28 08:56:10 +00001065 rx = output.get_capture(n_rx)
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001066 return rx
1067
Paul Vinciguerraeb414432019-02-20 09:01:14 -08001068 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1069 self.pg_send(intf, pkts)
Paul Vinciguerra8aeb2202019-01-07 16:29:26 -08001070 rx = output.get_capture(len(pkts))
1071 outputs = [output]
Neale Ranns93cc3ee2018-10-10 07:22:51 -07001072 if not timeout:
1073 timeout = 1
1074 for i in self.pg_interfaces:
1075 if i not in outputs:
1076 i.get_capture(0, timeout=timeout)
1077 i.assert_nothing_captured()
1078 timeout = 0.1
1079
Neale Ranns52fae862018-01-08 04:41:42 -08001080 return rx
1081
Paul Vinciguerra087c8112018-12-15 08:03:09 -08001082 def runTest(self):
1083 """ unittest calls runTest when TestCase is instantiated without a
1084 test case. Use case: Writing unittests against VppTestCase"""
1085 pass
1086
Damjan Marionf56b77a2016-10-03 19:44:57 +02001087
juraj.linkes184870a2018-07-16 14:22:01 +02001088def get_testcase_doc_name(test):
1089 return getdoc(test.__class__).splitlines()[0]
1090
1091
Ole Trøan5ba91592018-11-22 10:01:09 +00001092def get_test_description(descriptions, test):
1093 short_description = test.shortDescription()
1094 if descriptions and short_description:
1095 return short_description
1096 else:
1097 return str(test)
1098
1099
juraj.linkes40dd73b2018-09-21 13:55:16 +02001100class TestCaseInfo(object):
1101 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1102 self.logger = logger
1103 self.tempdir = tempdir
1104 self.vpp_pid = vpp_pid
1105 self.vpp_bin_path = vpp_bin_path
1106 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001107
1108
Damjan Marionf56b77a2016-10-03 19:44:57 +02001109class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001110 """
1111 @property result_string
1112 String variable to store the test case result string.
1113 @property errors
1114 List variable containing 2-tuples of TestCase instances and strings
1115 holding formatted tracebacks. Each tuple represents a test which
1116 raised an unexpected exception.
1117 @property failures
1118 List variable containing 2-tuples of TestCase instances and strings
1119 holding formatted tracebacks. Each tuple represents a test where
1120 a failure was explicitly signalled using the TestCase.assert*()
1121 methods.
1122 """
1123
juraj.linkes40dd73b2018-09-21 13:55:16 +02001124 failed_test_cases_info = set()
1125 core_crash_test_cases_info = set()
1126 current_test_case_info = None
1127
Paul Vinciguerra1ec06ff2019-01-16 11:12:50 -08001128 def __init__(self, stream=None, descriptions=None, verbosity=None,
1129 runner=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001130 """
Klement Sekerada505f62017-01-04 12:58:53 +01001131 :param stream File descriptor to store where to report test results.
1132 Set to the standard error stream by default.
1133 :param descriptions Boolean variable to store information if to use
1134 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001135 :param verbosity Integer variable to store required verbosity level.
1136 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001137 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001138 self.stream = stream
1139 self.descriptions = descriptions
1140 self.verbosity = verbosity
1141 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001142 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001143
Damjan Marionf56b77a2016-10-03 19:44:57 +02001144 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001145 """
1146 Record a test succeeded result
1147
1148 :param test:
1149
1150 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001151 if self.current_test_case_info:
1152 self.current_test_case_info.logger.debug(
1153 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1154 test._testMethodName,
1155 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001156 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001157 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001158
juraj.linkescae64f82018-09-19 15:01:47 +02001159 self.send_result_through_pipe(test, PASS)
1160
Klement Sekeraf62ae122016-10-11 11:47:09 +02001161 def addSkip(self, test, reason):
1162 """
1163 Record a test skipped.
1164
1165 :param test:
1166 :param reason:
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 "--- addSkip() %s.%s(%s) called, reason is %s" %
1172 (test.__class__.__name__, test._testMethodName,
1173 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001174 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001175 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001176
juraj.linkescae64f82018-09-19 15:01:47 +02001177 self.send_result_through_pipe(test, SKIP)
1178
juraj.linkes40dd73b2018-09-21 13:55:16 +02001179 def symlink_failed(self):
1180 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001181 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001182 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001183 link_path = os.path.join(
1184 failed_dir,
1185 '%s-FAILED' %
1186 os.path.basename(self.current_test_case_info.tempdir))
1187 if self.current_test_case_info.logger:
1188 self.current_test_case_info.logger.debug(
1189 "creating a link to the failed test")
1190 self.current_test_case_info.logger.debug(
1191 "os.symlink(%s, %s)" %
1192 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001193 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001194 if self.current_test_case_info.logger:
1195 self.current_test_case_info.logger.debug(
1196 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001197 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001198 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001199
Klement Sekeraf413bef2017-08-15 07:09:02 +02001200 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001201 if self.current_test_case_info.logger:
1202 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001203
juraj.linkescae64f82018-09-19 15:01:47 +02001204 def send_result_through_pipe(self, test, result):
1205 if hasattr(self, 'test_framework_result_pipe'):
1206 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001207 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001208 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001209
juraj.linkes40dd73b2018-09-21 13:55:16 +02001210 def log_error(self, test, err, fn_name):
1211 if self.current_test_case_info:
1212 if isinstance(test, unittest.suite._ErrorHolder):
1213 test_name = test.description
1214 else:
1215 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1216 test._testMethodName,
1217 test._testMethodDoc)
1218 self.current_test_case_info.logger.debug(
1219 "--- %s() %s called, err is %s" %
1220 (fn_name, test_name, err))
1221 self.current_test_case_info.logger.debug(
1222 "formatted exception is:\n%s" %
1223 "".join(format_exception(*err)))
1224
1225 def add_error(self, test, err, unittest_fn, error_type):
1226 if error_type == FAIL:
1227 self.log_error(test, err, 'addFailure')
1228 error_type_str = colorize("FAIL", RED)
1229 elif error_type == ERROR:
1230 self.log_error(test, err, 'addError')
1231 error_type_str = colorize("ERROR", RED)
1232 else:
1233 raise Exception('Error type %s cannot be used to record an '
1234 'error or a failure' % error_type)
1235
1236 unittest_fn(self, test, err)
1237 if self.current_test_case_info:
1238 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1239 (error_type_str,
1240 self.current_test_case_info.tempdir)
1241 self.symlink_failed()
1242 self.failed_test_cases_info.add(self.current_test_case_info)
1243 if is_core_present(self.current_test_case_info.tempdir):
1244 if not self.current_test_case_info.core_crash_test:
1245 if isinstance(test, unittest.suite._ErrorHolder):
1246 test_name = str(test)
1247 else:
Paul Vinciguerraea2450f2019-03-06 08:23:58 -08001248 test_name = "'{!s}' ({!s})".format(
juraj.linkes40dd73b2018-09-21 13:55:16 +02001249 get_testcase_doc_name(test), test.id())
1250 self.current_test_case_info.core_crash_test = test_name
1251 self.core_crash_test_cases_info.add(
1252 self.current_test_case_info)
1253 else:
1254 self.result_string = '%s [no temp dir]' % error_type_str
1255
1256 self.send_result_through_pipe(test, error_type)
1257
Damjan Marionf56b77a2016-10-03 19:44:57 +02001258 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001259 """
1260 Record a test failed result
1261
1262 :param test:
1263 :param err: error message
1264
1265 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001266 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001267
Damjan Marionf56b77a2016-10-03 19:44:57 +02001268 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001269 """
1270 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001271
Klement Sekeraf62ae122016-10-11 11:47:09 +02001272 :param test:
1273 :param err: error message
1274
1275 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001276 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001277
Damjan Marionf56b77a2016-10-03 19:44:57 +02001278 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001279 """
1280 Get test description
1281
1282 :param test:
1283 :returns: test description
1284
1285 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001286 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001287
Damjan Marionf56b77a2016-10-03 19:44:57 +02001288 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001289 """
1290 Start a test
1291
1292 :param test:
1293
1294 """
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001295
1296 def print_header(test):
1297 if not hasattr(test.__class__, '_header_printed'):
1298 print(double_line_delim)
1299 print(colorize(getdoc(test).splitlines()[0], GREEN))
1300 print(double_line_delim)
1301 test.__class__._header_printed = True
1302
1303 print_header(test)
juraj.linkes40dd73b2018-09-21 13:55:16 +02001304
Damjan Marionf56b77a2016-10-03 19:44:57 +02001305 unittest.TestResult.startTest(self, test)
1306 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001307 self.stream.writeln(
1308 "Starting " + self.getDescription(test) + " ...")
1309 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001310
Damjan Marionf56b77a2016-10-03 19:44:57 +02001311 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001312 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001313 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001314
1315 :param test:
1316
1317 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001318 unittest.TestResult.stopTest(self, test)
1319 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001320 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001321 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001322 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001323 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001324 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001325 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001326 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001327
1328 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001329
Damjan Marionf56b77a2016-10-03 19:44:57 +02001330 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001331 """
1332 Print errors from running the test case
1333 """
juraj.linkesabec0122018-11-16 17:28:56 +01001334 if len(self.errors) > 0 or len(self.failures) > 0:
1335 self.stream.writeln()
1336 self.printErrorList('ERROR', self.errors)
1337 self.printErrorList('FAIL', self.failures)
1338
1339 # ^^ that is the last output from unittest before summary
1340 if not self.runner.print_summary:
1341 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1342 self.stream = devnull
1343 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001344
Damjan Marionf56b77a2016-10-03 19:44:57 +02001345 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001346 """
1347 Print error list to the output stream together with error type
1348 and test case description.
1349
1350 :param flavour: error type
1351 :param errors: iterable errors
1352
1353 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001354 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001355 self.stream.writeln(double_line_delim)
1356 self.stream.writeln("%s: %s" %
1357 (flavour, self.getDescription(test)))
1358 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001359 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001360
1361
Damjan Marionf56b77a2016-10-03 19:44:57 +02001362class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001363 """
Klement Sekera104543f2017-02-03 07:29:43 +01001364 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001365 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001366
Klement Sekeraf62ae122016-10-11 11:47:09 +02001367 @property
1368 def resultclass(self):
1369 """Class maintaining the results of the tests"""
1370 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001371
juraj.linkes184870a2018-07-16 14:22:01 +02001372 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001373 result_pipe=None, failfast=False, buffer=False,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001374 resultclass=None, print_summary=True, **kwargs):
Klement Sekera7a161da2017-01-17 13:42:48 +01001375 # ignore stream setting here, use hard-coded stdout to be in sync
1376 # with prints from VppTestCase methods ...
1377 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1378 verbosity, failfast, buffer,
Paul Vinciguerra98894022019-01-13 21:32:37 -08001379 resultclass, **kwargs)
juraj.linkesccfead62018-11-21 13:20:43 +01001380 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001381
juraj.linkesabec0122018-11-16 17:28:56 +01001382 self.orig_stream = self.stream
1383 self.resultclass.test_framework_result_pipe = result_pipe
1384
1385 self.print_summary = print_summary
1386
1387 def _makeResult(self):
1388 return self.resultclass(self.stream,
1389 self.descriptions,
1390 self.verbosity,
1391 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001392
Damjan Marionf56b77a2016-10-03 19:44:57 +02001393 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001394 """
1395 Run the tests
1396
1397 :param test:
1398
1399 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001400 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001401
1402 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001403 if not self.print_summary:
1404 self.stream = self.orig_stream
1405 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001406 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001407
1408
1409class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001410 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001411 self.logger = logger
1412 self.args = args
1413 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001414 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001415 super(Worker, self).__init__()
1416
1417 def run(self):
1418 executable = self.args[0]
1419 self.logger.debug("Running executable w/args `%s'" % self.args)
1420 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001421 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001422 env["CK_LOG_FILE_NAME"] = "-"
1423 self.process = subprocess.Popen(
1424 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1425 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1426 out, err = self.process.communicate()
1427 self.logger.debug("Finished running `%s'" % executable)
1428 self.logger.info("Return code is `%s'" % self.process.returncode)
1429 self.logger.info(single_line_delim)
1430 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1431 self.logger.info(single_line_delim)
1432 self.logger.info(out)
1433 self.logger.info(single_line_delim)
1434 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1435 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001436 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001437 self.logger.info(single_line_delim)
1438 self.result = self.process.returncode
Paul Vinciguerradd3c5d22019-01-13 16:09:10 -08001439
1440if __name__ == '__main__':
1441 pass