blob: 5eeb5187041124a1981742d3862095965dabb16a [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
21from scapy.packet import Raw
22from hook import StepHook, PollHook, VppDiedError
Paul Vinciguerra919efad2018-12-17 21:43:43 -080023from vpp_config import VppTestCaseVppConfig
24from vpp_interface import VppInterface
Ole Trøan162989e2018-11-26 10:27:50 +000025from vpp_sub_interface import VppSubInterface
Paul Vinciguerra919efad2018-12-17 21:43:43 -080026from vpp_pg_interface import VppPGInterface
Ole Trøan162989e2018-11-26 10:27:50 +000027from vpp_lo_interface import VppLoInterface
28from vpp_papi_provider import VppPapiProvider
29from vpp_papi.vpp_stats import VPPStats
30from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
31 get_logger, colorize
32from vpp_object import VppObjectRegistry
33from util import ppp, is_core_present
Klement Sekerad81ae412018-05-16 10:52:54 +020034from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
35from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
36from scapy.layers.inet6 import ICMPv6EchoReply
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -080037
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010038if os.name == 'posix' and sys.version_info[0] < 3:
39 # using subprocess32 is recommended by python official documentation
40 # @ https://docs.python.org/2/library/subprocess.html
41 import subprocess32 as subprocess
42else:
43 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020044
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -080045# Python2/3 compatible
46try:
47 input = raw_input
48except NameError:
49 pass
50
juraj.linkescae64f82018-09-19 15:01:47 +020051PASS = 0
52FAIL = 1
53ERROR = 2
54SKIP = 3
55TEST_RUN = 4
56
Klement Sekeraebbaf552018-02-17 13:41:33 +010057debug_framework = False
58if os.getenv('TEST_DEBUG', "0") == "1":
59 debug_framework = True
60 import debug_internal
61
Klement Sekeraf62ae122016-10-11 11:47:09 +020062"""
63 Test framework module.
64
65 The module provides a set of tools for constructing and running tests and
66 representing the results.
67"""
68
Klement Sekeraf62ae122016-10-11 11:47:09 +020069
Damjan Marionf56b77a2016-10-03 19:44:57 +020070class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020071 """Private class to create packet info object.
72
73 Help process information about the next packet.
74 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020075 """
Matej Klotton86d87c42016-11-11 11:38:55 +010076 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020077 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010078 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020079 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010080 #: Store the index of the destination packet generator interface
81 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020082 dst = -1
Pavel Kotucek59dda062017-03-02 15:22:47 +010083 #: Store expected ip version
84 ip = -1
85 #: Store expected upper protocol
86 proto = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010087 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020088 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020089
Matej Klotton16a14cd2016-12-07 15:09:13 +010090 def __eq__(self, other):
91 index = self.index == other.index
92 src = self.src == other.src
93 dst = self.dst == other.dst
94 data = self.data == other.data
95 return index and src and dst and data
96
Klement Sekeraf62ae122016-10-11 11:47:09 +020097
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010098def pump_output(testclass):
99 """ pump output from vpp stdout/stderr to proper queues """
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100100 stdout_fragment = ""
101 stderr_fragment = ""
Neale Ranns16782362018-07-23 05:35:56 -0400102 while not testclass.pump_thread_stop_flag.is_set():
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100103 readable = select.select([testclass.vpp.stdout.fileno(),
104 testclass.vpp.stderr.fileno(),
105 testclass.pump_thread_wakeup_pipe[0]],
106 [], [])[0]
107 if testclass.vpp.stdout.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100108 read = os.read(testclass.vpp.stdout.fileno(), 102400)
109 if len(read) > 0:
110 split = read.splitlines(True)
111 if len(stdout_fragment) > 0:
112 split[0] = "%s%s" % (stdout_fragment, split[0])
113 if len(split) > 0 and split[-1].endswith("\n"):
114 limit = None
115 else:
116 limit = -1
117 stdout_fragment = split[-1]
118 testclass.vpp_stdout_deque.extend(split[:limit])
119 if not testclass.cache_vpp_output:
120 for line in split[:limit]:
121 testclass.logger.debug(
122 "VPP STDOUT: %s" % line.rstrip("\n"))
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100123 if testclass.vpp.stderr.fileno() in readable:
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100124 read = os.read(testclass.vpp.stderr.fileno(), 102400)
125 if len(read) > 0:
126 split = read.splitlines(True)
127 if len(stderr_fragment) > 0:
128 split[0] = "%s%s" % (stderr_fragment, split[0])
Ole Troan7f991832018-12-06 17:35:12 +0100129 if len(split) > 0 and split[-1].endswith(b"\n"):
Klement Sekera6a6f4f72017-11-09 09:16:39 +0100130 limit = None
131 else:
132 limit = -1
133 stderr_fragment = split[-1]
134 testclass.vpp_stderr_deque.extend(split[:limit])
135 if not testclass.cache_vpp_output:
136 for line in split[:limit]:
137 testclass.logger.debug(
138 "VPP STDERR: %s" % line.rstrip("\n"))
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800139 # ignoring the dummy pipe here intentionally - the
140 # flag will take care of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +0200141
142
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800143def _is_skip_aarch64_set():
juraj.linkes68ebc832018-11-29 09:37:08 +0100144 return os.getenv('SKIP_AARCH64', 'n').lower() in ('yes', 'y', '1')
145
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800146is_skip_aarch64_set = _is_skip_aarch64_set()
juraj.linkes68ebc832018-11-29 09:37:08 +0100147
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800148
149def _is_platform_aarch64():
juraj.linkes68ebc832018-11-29 09:37:08 +0100150 return platform.machine() == 'aarch64'
151
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800152is_platform_aarch64 = _is_platform_aarch64()
juraj.linkes68ebc832018-11-29 09:37:08 +0100153
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800154
155def _running_extended_tests():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100156 s = os.getenv("EXTENDED_TESTS", "n")
157 return True if s.lower() in ("y", "yes", "1") else False
Klement Sekera87134932017-03-07 11:39:27 +0100158
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800159running_extended_tests = _running_extended_tests()
Klement Sekera87134932017-03-07 11:39:27 +0100160
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800161
162def _running_on_centos():
Klement Sekera13a83ef2018-03-21 12:35:51 +0100163 os_id = os.getenv("OS_ID", "")
164 return True if "centos" in os_id.lower() else False
Klement Sekerad3e671e2017-09-29 12:36:37 +0200165
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800166running_on_centos = _running_on_centos
167
Klement Sekerad3e671e2017-09-29 12:36:37 +0200168
Klement Sekera909a6a12017-08-08 04:33:53 +0200169class KeepAliveReporter(object):
170 """
171 Singleton object which reports test start to parent process
172 """
173 _shared_state = {}
174
175 def __init__(self):
176 self.__dict__ = self._shared_state
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800177 self._pipe = None
Klement Sekera909a6a12017-08-08 04:33:53 +0200178
179 @property
180 def pipe(self):
181 return self._pipe
182
183 @pipe.setter
184 def pipe(self, pipe):
Paul Vinciguerrac7b03fe2018-11-18 08:17:34 -0800185 if self._pipe is not None:
Klement Sekera909a6a12017-08-08 04:33:53 +0200186 raise Exception("Internal error - pipe should only be set once.")
187 self._pipe = pipe
188
juraj.linkes40dd73b2018-09-21 13:55:16 +0200189 def send_keep_alive(self, test, desc=None):
Klement Sekera909a6a12017-08-08 04:33:53 +0200190 """
191 Write current test tmpdir & desc to keep-alive pipe to signal liveness
192 """
Klement Sekera3f6ff192017-08-11 06:56:05 +0200193 if self.pipe is None:
194 # if not running forked..
195 return
196
Klement Sekera909a6a12017-08-08 04:33:53 +0200197 if isclass(test):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200198 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
Klement Sekera909a6a12017-08-08 04:33:53 +0200199 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200200 desc = test.id()
Klement Sekera909a6a12017-08-08 04:33:53 +0200201
Dave Wallacee2efd122017-09-30 22:04:21 -0400202 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
Klement Sekera909a6a12017-08-08 04:33:53 +0200203
204
Damjan Marionf56b77a2016-10-03 19:44:57 +0200205class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +0100206 """This subclass is a base class for VPP test cases that are implemented as
207 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200208 """
209
Paul Vinciguerra919efad2018-12-17 21:43:43 -0800210 CLI_LISTEN_DEFAULT = 'localhost:5002'
211 config = VppTestCaseVppConfig()
Pavel Kotuceke88865d2018-11-28 07:42:11 +0100212
Klement Sekeraf62ae122016-10-11 11:47:09 +0200213 @property
214 def packet_infos(self):
215 """List of packet infos"""
216 return self._packet_infos
217
Klement Sekeradab231a2016-12-21 08:50:14 +0100218 @classmethod
219 def get_packet_count_for_if_idx(cls, dst_if_index):
220 """Get the number of packet info for specified destination if index"""
221 if dst_if_index in cls._packet_count_for_dst_if_idx:
222 return cls._packet_count_for_dst_if_idx[dst_if_index]
223 else:
224 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200225
226 @classmethod
227 def instance(cls):
228 """Return the instance of this testcase"""
229 return cls.test_instance
230
Damjan Marionf56b77a2016-10-03 19:44:57 +0200231 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200232 def set_debug_flags(cls, d):
233 cls.debug_core = False
234 cls.debug_gdb = False
235 cls.debug_gdbserver = False
236 if d is None:
237 return
238 dl = d.lower()
239 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200240 cls.debug_core = True
241 elif dl == "gdb":
242 cls.debug_gdb = True
243 elif dl == "gdbserver":
244 cls.debug_gdbserver = True
245 else:
246 raise Exception("Unrecognized DEBUG option: '%s'" % d)
247
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800248 @staticmethod
249 def get_least_used_cpu():
juraj.linkes184870a2018-07-16 14:22:01 +0200250 cpu_usage_list = [set(range(psutil.cpu_count()))]
251 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
252 if 'vpp_main' == p.info['name']]
253 for vpp_process in vpp_processes:
254 for cpu_usage_set in cpu_usage_list:
255 try:
256 cpu_num = vpp_process.cpu_num()
257 if cpu_num in cpu_usage_set:
258 cpu_usage_set_index = cpu_usage_list.index(
259 cpu_usage_set)
260 if cpu_usage_set_index == len(cpu_usage_list) - 1:
261 cpu_usage_list.append({cpu_num})
262 else:
263 cpu_usage_list[cpu_usage_set_index + 1].add(
264 cpu_num)
265 cpu_usage_set.remove(cpu_num)
266 break
267 except psutil.NoSuchProcess:
268 pass
269
270 for cpu_usage_set in cpu_usage_list:
271 if len(cpu_usage_set) > 0:
272 min_usage_set = cpu_usage_set
273 break
274
275 return random.choice(tuple(min_usage_set))
276
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800277 @classmethod
juraj.linkes40dd73b2018-09-21 13:55:16 +0200278 def print_header(cls):
279 if not hasattr(cls, '_header_printed'):
280 print(double_line_delim)
281 print(colorize(getdoc(cls).splitlines()[0], GREEN))
282 print(double_line_delim)
283 cls._header_printed = True
284
juraj.linkes184870a2018-07-16 14:22:01 +0200285 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200286 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200287 """ Set-up the test case class based on environment variables """
Klement Sekera13a83ef2018-03-21 12:35:51 +0100288 s = os.getenv("STEP", "n")
289 cls.step = True if s.lower() in ("y", "yes", "1") else False
290 d = os.getenv("DEBUG", None)
291 c = os.getenv("CACHE_OUTPUT", "1")
292 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
Klement Sekera277b89c2016-10-28 13:20:27 +0200293 cls.set_debug_flags(d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100294 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
295 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
Klement Sekera47e275b2017-03-21 08:21:25 +0100296 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
297 plugin_path = None
298 if cls.plugin_path is not None:
299 if cls.extern_plugin_path is not None:
300 plugin_path = "%s:%s" % (
301 cls.plugin_path, cls.extern_plugin_path)
Klement Sekera6abbc282017-03-24 05:47:15 +0100302 else:
303 plugin_path = cls.plugin_path
Klement Sekera47e275b2017-03-21 08:21:25 +0100304 elif cls.extern_plugin_path is not None:
305 plugin_path = cls.extern_plugin_path
Paul Vinciguerra919efad2018-12-17 21:43:43 -0800306
Klement Sekera01bbbe92016-11-02 09:25:05 +0100307 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
Paul Vinciguerra919efad2018-12-17 21:43:43 -0800308 cls.config.add('unix', 'cli-listen', cls.CLI_LISTEN_DEFAULT)
309
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100310 coredump_size = None
Klement Sekera13a83ef2018-03-21 12:35:51 +0100311 size = os.getenv("COREDUMP_SIZE")
Paul Vinciguerra919efad2018-12-17 21:43:43 -0800312 cls.config.add('unix', 'coredump-size',
313 size if size is not None else 'unlimited')
juraj.linkes184870a2018-07-16 14:22:01 +0200314
Paul Vinciguerra919efad2018-12-17 21:43:43 -0800315 cls.config.add('unix', 'runtime-dir', cls.tempdir)
316 cls.config.add('api-segment', 'prefix', cls.shm_prefix)
317 cls.config.add('cpu', 'main-core', str(cls.get_least_used_cpu()))
318 cls.config.add('statseg', 'socket-name', cls.stats_sock)
juraj.linkes184870a2018-07-16 14:22:01 +0200319
Klement Sekera47e275b2017-03-21 08:21:25 +0100320 if plugin_path is not None:
Paul Vinciguerra919efad2018-12-17 21:43:43 -0800321 cls.config.add('plugins', 'path', plugin_path)
322 cls.config.add_plugin('dpdk_plugin.so', 'disable')
323 cls.config.add_plugin('unittest_plugin.so', 'enable')
324
325 cls.vpp_cmdline = [cls.vpp_bin] + cls.config.shlex()
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100326 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
327 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
Klement Sekera277b89c2016-10-28 13:20:27 +0200328
329 @classmethod
330 def wait_for_enter(cls):
331 if cls.debug_gdbserver:
332 print(double_line_delim)
333 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
334 elif cls.debug_gdb:
335 print(double_line_delim)
336 print("Spawned VPP with PID: %d" % cls.vpp.pid)
337 else:
338 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
339 return
340 print(single_line_delim)
341 print("You can debug the VPP using e.g.:")
342 if cls.debug_gdbserver:
343 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
344 print("Now is the time to attach a gdb by running the above "
345 "command, set up breakpoints etc. and then resume VPP from "
346 "within gdb by issuing the 'continue' command")
347 elif cls.debug_gdb:
348 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
349 print("Now is the time to attach a gdb by running the above "
350 "command and set up breakpoints etc.")
351 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800352 input("Press ENTER to continue running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200353
354 @classmethod
355 def run_vpp(cls):
356 cmdline = cls.vpp_cmdline
357
358 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100359 gdbserver = '/usr/bin/gdbserver'
360 if not os.path.isfile(gdbserver) or \
361 not os.access(gdbserver, os.X_OK):
362 raise Exception("gdbserver binary '%s' does not exist or is "
363 "not executable" % gdbserver)
364
365 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200366 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
367
Klement Sekera931be3a2016-11-03 05:36:01 +0100368 try:
369 cls.vpp = subprocess.Popen(cmdline,
370 stdout=subprocess.PIPE,
371 stderr=subprocess.PIPE,
372 bufsize=1)
Paul Vinciguerra61e63bf2018-11-24 21:19:38 -0800373 except subprocess.CalledProcessError as e:
Klement Sekera931be3a2016-11-03 05:36:01 +0100374 cls.logger.critical("Couldn't start vpp: %s" % e)
375 raise
376
Klement Sekera277b89c2016-10-28 13:20:27 +0200377 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100378
Damjan Marionf56b77a2016-10-03 19:44:57 +0200379 @classmethod
Klement Sekera611864f2018-09-26 11:19:00 +0200380 def wait_for_stats_socket(cls):
381 deadline = time.time() + 3
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800382 ok = False
383 while time.time() < deadline or \
384 cls.debug_gdb or cls.debug_gdbserver:
Klement Sekera611864f2018-09-26 11:19:00 +0200385 if os.path.exists(cls.stats_sock):
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800386 ok = True
Klement Sekera611864f2018-09-26 11:19:00 +0200387 break
Paul Vinciguerradd4b2bb2018-11-15 08:13:03 -0800388 time.sleep(0.8)
389 if not ok:
390 cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
Klement Sekera611864f2018-09-26 11:19:00 +0200391
392 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200393 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200394 """
395 Perform class setup before running the testcase
396 Remove shared memory files, start vpp and connect the vpp-api
397 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100398 gc.collect() # run garbage collection first
Klement Sekera96867ba2018-02-02 11:27:53 +0100399 random.seed()
Paul Vinciguerra86ebba62018-11-21 09:28:32 -0800400 cls.print_header()
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100401 cls.logger = get_logger(cls.__name__)
402 if hasattr(cls, 'parallel_handler'):
403 cls.logger.addHandler(cls.parallel_handler)
juraj.linkes3d9b92a2018-11-21 13:13:39 +0100404 cls.logger.propagate = False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200405 cls.tempdir = tempfile.mkdtemp(
Klement Sekeraf413bef2017-08-15 07:09:02 +0200406 prefix='vpp-unittest-%s-' % cls.__name__)
Klement Sekera611864f2018-09-26 11:19:00 +0200407 cls.stats_sock = "%s/stats.sock" % cls.tempdir
Klement Sekera027dbd52017-04-11 06:01:53 +0200408 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
409 cls.file_handler.setFormatter(
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100410 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
411 datefmt="%H:%M:%S"))
Klement Sekera027dbd52017-04-11 06:01:53 +0200412 cls.file_handler.setLevel(DEBUG)
413 cls.logger.addHandler(cls.file_handler)
juraj.linkes184870a2018-07-16 14:22:01 +0200414 cls.shm_prefix = os.path.basename(cls.tempdir)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200415 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200416 cls.logger.info("Temporary dir is %s, shm prefix is %s",
417 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200418 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100419 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100420 cls._captures = []
421 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200422 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100423 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100424 cls.registry = VppObjectRegistry()
Klement Sekera3747c752017-04-10 06:30:17 +0200425 cls.vpp_startup_failed = False
Klement Sekera909a6a12017-08-08 04:33:53 +0200426 cls.reporter = KeepAliveReporter()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200427 # need to catch exceptions here because if we raise, then the cleanup
428 # doesn't get called and we might end with a zombie vpp
429 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200430 cls.run_vpp()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200431 cls.reporter.send_keep_alive(cls, 'setUpClass')
432 VppTestResult.current_test_case_info = TestCaseInfo(
433 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
Klement Sekerae4504c62016-12-08 10:16:41 +0100434 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100435 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100436 cls.pump_thread_stop_flag = Event()
437 cls.pump_thread_wakeup_pipe = os.pipe()
438 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100439 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100440 cls.pump_thread.start()
Klement Sekera611864f2018-09-26 11:19:00 +0200441 if cls.debug_gdb or cls.debug_gdbserver:
442 read_timeout = 0
443 else:
444 read_timeout = 5
445 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
446 read_timeout)
Klement Sekera085f5c02016-11-24 01:59:16 +0100447 if cls.step:
448 hook = StepHook(cls)
449 else:
450 hook = PollHook(cls)
451 cls.vapi.register_hook(hook)
Klement Sekera611864f2018-09-26 11:19:00 +0200452 cls.wait_for_stats_socket()
453 cls.statistics = VPPStats(socketname=cls.stats_sock)
Klement Sekera3747c752017-04-10 06:30:17 +0200454 try:
455 hook.poll_vpp()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100456 except VppDiedError:
Klement Sekera3747c752017-04-10 06:30:17 +0200457 cls.vpp_startup_failed = True
458 cls.logger.critical(
459 "VPP died shortly after startup, check the"
460 " output to standard error for possible cause")
461 raise
Klement Sekera085f5c02016-11-24 01:59:16 +0100462 try:
463 cls.vapi.connect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100464 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100465 try:
466 cls.vapi.disconnect()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100467 except Exception:
Klement Sekera4c533132018-02-22 11:41:12 +0100468 pass
Klement Sekera085f5c02016-11-24 01:59:16 +0100469 if cls.debug_gdbserver:
470 print(colorize("You're running VPP inside gdbserver but "
471 "VPP-API connection failed, did you forget "
472 "to 'continue' VPP from within gdb?", RED))
473 raise
Klement Sekera13a83ef2018-03-21 12:35:51 +0100474 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100475 try:
476 cls.quit()
Klement Sekera13a83ef2018-03-21 12:35:51 +0100477 except Exception:
Klement Sekera085f5c02016-11-24 01:59:16 +0100478 pass
Klement Sekera13a83ef2018-03-21 12:35:51 +0100479 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200480
Damjan Marionf56b77a2016-10-03 19:44:57 +0200481 @classmethod
482 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200483 """
484 Disconnect vpp-api, kill vpp and cleanup shared memory files
485 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200486 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
487 cls.vpp.poll()
488 if cls.vpp.returncode is None:
489 print(double_line_delim)
490 print("VPP or GDB server is still running")
491 print(single_line_delim)
Paul Vinciguerra852f5ef2018-12-15 10:16:35 -0800492 input("When done debugging, press ENTER to kill the "
493 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200494
juraj.linkes184870a2018-07-16 14:22:01 +0200495 # first signal that we want to stop the pump thread, then wake it up
496 if hasattr(cls, 'pump_thread_stop_flag'):
497 cls.pump_thread_stop_flag.set()
498 if hasattr(cls, 'pump_thread_wakeup_pipe'):
Ole Troan7f991832018-12-06 17:35:12 +0100499 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100500 if hasattr(cls, 'pump_thread'):
501 cls.logger.debug("Waiting for pump thread to stop")
502 cls.pump_thread.join()
503 if hasattr(cls, 'vpp_stderr_reader_thread'):
504 cls.logger.debug("Waiting for stdderr pump to stop")
505 cls.vpp_stderr_reader_thread.join()
506
Klement Sekeraf62ae122016-10-11 11:47:09 +0200507 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100508 if hasattr(cls, 'vapi'):
509 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100510 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200511 cls.vpp.poll()
512 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100513 cls.logger.debug("Sending TERM to vpp")
Klement Sekera611864f2018-09-26 11:19:00 +0200514 cls.vpp.kill()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100515 cls.logger.debug("Waiting for vpp to die")
516 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200517 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200518
Klement Sekera3747c752017-04-10 06:30:17 +0200519 if cls.vpp_startup_failed:
520 stdout_log = cls.logger.info
521 stderr_log = cls.logger.critical
522 else:
523 stdout_log = cls.logger.info
524 stderr_log = cls.logger.info
525
Klement Sekerae4504c62016-12-08 10:16:41 +0100526 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200527 stdout_log(single_line_delim)
528 stdout_log('VPP output to stdout while running %s:', cls.__name__)
529 stdout_log(single_line_delim)
Klement Sekerae4504c62016-12-08 10:16:41 +0100530 vpp_output = "".join(cls.vpp_stdout_deque)
Klement Sekera027dbd52017-04-11 06:01:53 +0200531 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
532 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200533 stdout_log('\n%s', vpp_output)
534 stdout_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200535
Klement Sekerae4504c62016-12-08 10:16:41 +0100536 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera3747c752017-04-10 06:30:17 +0200537 stderr_log(single_line_delim)
538 stderr_log('VPP output to stderr while running %s:', cls.__name__)
539 stderr_log(single_line_delim)
Ole Troan7f991832018-12-06 17:35:12 +0100540 vpp_output = "".join(str(cls.vpp_stderr_deque))
Klement Sekera027dbd52017-04-11 06:01:53 +0200541 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
542 f.write(vpp_output)
Klement Sekera3747c752017-04-10 06:30:17 +0200543 stderr_log('\n%s', vpp_output)
544 stderr_log(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200545
Damjan Marionf56b77a2016-10-03 19:44:57 +0200546 @classmethod
547 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200548 """ Perform final cleanup after running all tests in this test-case """
juraj.linkes40dd73b2018-09-21 13:55:16 +0200549 cls.reporter.send_keep_alive(cls, 'tearDownClass')
Damjan Marionf56b77a2016-10-03 19:44:57 +0200550 cls.quit()
Klement Sekera027dbd52017-04-11 06:01:53 +0200551 cls.file_handler.close()
Klement Sekeraebbaf552018-02-17 13:41:33 +0100552 cls.reset_packet_infos()
553 if debug_framework:
554 debug_internal.on_tear_down_class(cls)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200555
Damjan Marionf56b77a2016-10-03 19:44:57 +0200556 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200557 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100558 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
559 (self.__class__.__name__, self._testMethodName,
560 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200561 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200562 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns88fc83e2017-04-05 08:11:14 -0700563 self.logger.info(self.vapi.ppcli("show interface"))
Jan49c0fca2016-10-26 15:44:27 +0200564 self.logger.info(self.vapi.ppcli("show hardware"))
Ole Troan73202102018-08-31 00:29:48 +0200565 self.logger.info(self.statistics.set_errors_str())
Jan49c0fca2016-10-26 15:44:27 +0200566 self.logger.info(self.vapi.ppcli("show run"))
Damjan Marion07a38572018-01-21 06:44:18 -0800567 self.logger.info(self.vapi.ppcli("show log"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100568 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500569 # Save/Dump VPP api trace log
570 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
571 tmp_api_trace = "/tmp/%s" % api_trace
572 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
573 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
574 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
575 vpp_api_trace_log))
576 os.rename(tmp_api_trace, vpp_api_trace_log)
Dave Wallace5ba58372018-02-13 16:14:06 -0500577 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
Dave Wallace90c55722017-02-16 11:25:26 -0500578 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100579 else:
580 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200581
Damjan Marionf56b77a2016-10-03 19:44:57 +0200582 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200583 """ Clear trace before running each test"""
Klement Sekera909a6a12017-08-08 04:33:53 +0200584 self.reporter.send_keep_alive(self)
Klement Sekerab91017a2017-02-09 06:04:36 +0100585 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
586 (self.__class__.__name__, self._testMethodName,
587 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100588 if self.vpp_dead:
589 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100590 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100591 self.vpp_stdout_deque.append(
592 "--- test setUp() for %s.%s(%s) starts here ---\n" %
593 (self.__class__.__name__, self._testMethodName,
594 self._testMethodDoc))
595 self.vpp_stderr_deque.append(
596 "--- test setUp() for %s.%s(%s) starts here ---\n" %
597 (self.__class__.__name__, self._testMethodName,
598 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200599 self.vapi.cli("clear trace")
600 # store the test instance inside the test class - so that objects
601 # holding the class can access instance methods (like assertEqual)
602 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200603
Damjan Marionf56b77a2016-10-03 19:44:57 +0200604 @classmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200605 def pg_enable_capture(cls, interfaces=None):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200606 """
607 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200608
Klement Sekera75e7d132017-09-20 08:26:30 +0200609 :param interfaces: iterable interface indexes (if None,
610 use self.pg_interfaces)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200611
Klement Sekeraf62ae122016-10-11 11:47:09 +0200612 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200613 if interfaces is None:
614 interfaces = cls.pg_interfaces
Klement Sekeraf62ae122016-10-11 11:47:09 +0200615 for i in interfaces:
616 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200617
Damjan Marionf56b77a2016-10-03 19:44:57 +0200618 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100619 def register_capture(cls, cap_name):
620 """ Register a capture in the testclass """
621 # add to the list of captures with current timestamp
622 cls._captures.append((time.time(), cap_name))
623 # filter out from zombies
624 cls._zombie_captures = [(stamp, name)
625 for (stamp, name) in cls._zombie_captures
626 if name != cap_name]
627
628 @classmethod
629 def pg_start(cls):
630 """ Remove any zombie captures and enable the packet generator """
631 # how long before capture is allowed to be deleted - otherwise vpp
632 # crashes - 100ms seems enough (this shouldn't be needed at all)
633 capture_ttl = 0.1
634 now = time.time()
635 for stamp, cap_name in cls._zombie_captures:
636 wait = stamp + capture_ttl - now
637 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100638 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100639 now = time.time()
640 cls.logger.debug("Removing zombie capture %s" % cap_name)
641 cls.vapi.cli('packet-generator delete %s' % cap_name)
642
Klement Sekeraf62ae122016-10-11 11:47:09 +0200643 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
644 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100645 cls._zombie_captures = cls._captures
646 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200647
Damjan Marionf56b77a2016-10-03 19:44:57 +0200648 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200649 def create_pg_interfaces(cls, interfaces):
650 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100651 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200652
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100653 :param interfaces: iterable indexes of the interfaces.
654 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200655
Klement Sekeraf62ae122016-10-11 11:47:09 +0200656 """
657 result = []
658 for i in interfaces:
659 intf = VppPGInterface(cls, i)
660 setattr(cls, intf.name, intf)
661 result.append(intf)
662 cls.pg_interfaces = result
663 return result
664
Matej Klotton0178d522016-11-04 11:11:44 +0100665 @classmethod
Klement Sekerab9ef2732018-06-24 22:49:33 +0200666 def create_loopback_interfaces(cls, count):
Matej Klotton0178d522016-11-04 11:11:44 +0100667 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100668 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100669
Klement Sekerab9ef2732018-06-24 22:49:33 +0200670 :param count: number of interfaces created.
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100671 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100672 """
Klement Sekerab9ef2732018-06-24 22:49:33 +0200673 result = [VppLoInterface(cls) for i in range(count)]
674 for intf in result:
Matej Klotton0178d522016-11-04 11:11:44 +0100675 setattr(cls, intf.name, intf)
Matej Klotton0178d522016-11-04 11:11:44 +0100676 cls.lo_interfaces = result
677 return result
678
Damjan Marionf56b77a2016-10-03 19:44:57 +0200679 @staticmethod
Klement Sekera75e7d132017-09-20 08:26:30 +0200680 def extend_packet(packet, size, padding=' '):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200681 """
Klement Sekera75e7d132017-09-20 08:26:30 +0200682 Extend packet to given size by padding with spaces or custom padding
Klement Sekeraf62ae122016-10-11 11:47:09 +0200683 NOTE: Currently works only when Raw layer is present.
684
685 :param packet: packet
686 :param size: target size
Klement Sekera75e7d132017-09-20 08:26:30 +0200687 :param padding: padding used to extend the payload
Klement Sekeraf62ae122016-10-11 11:47:09 +0200688
689 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200690 packet_len = len(packet) + 4
691 extend = size - packet_len
692 if extend > 0:
Klement Sekera75e7d132017-09-20 08:26:30 +0200693 num = (extend / len(padding)) + 1
694 packet[Raw].load += (padding * num)[:extend]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200695
Klement Sekeradab231a2016-12-21 08:50:14 +0100696 @classmethod
697 def reset_packet_infos(cls):
698 """ Reset the list of packet info objects and packet counts to zero """
699 cls._packet_infos = {}
700 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200701
Klement Sekeradab231a2016-12-21 08:50:14 +0100702 @classmethod
703 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200704 """
705 Create packet info object containing the source and destination indexes
706 and add it to the testcase's packet info list
707
Klement Sekeradab231a2016-12-21 08:50:14 +0100708 :param VppInterface src_if: source interface
709 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200710
711 :returns: _PacketInfo object
712
713 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200714 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100715 info.index = len(cls._packet_infos)
716 info.src = src_if.sw_if_index
717 info.dst = dst_if.sw_if_index
718 if isinstance(dst_if, VppSubInterface):
719 dst_idx = dst_if.parent.sw_if_index
720 else:
721 dst_idx = dst_if.sw_if_index
722 if dst_idx in cls._packet_count_for_dst_if_idx:
723 cls._packet_count_for_dst_if_idx[dst_idx] += 1
724 else:
725 cls._packet_count_for_dst_if_idx[dst_idx] = 1
726 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200728
Damjan Marionf56b77a2016-10-03 19:44:57 +0200729 @staticmethod
730 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200731 """
732 Convert _PacketInfo object to packet payload
733
734 :param info: _PacketInfo object
735
736 :returns: string containing serialized data from packet info
737 """
Pavel Kotucek59dda062017-03-02 15:22:47 +0100738 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
739 info.ip, info.proto)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200740
Damjan Marionf56b77a2016-10-03 19:44:57 +0200741 @staticmethod
742 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200743 """
744 Convert packet payload to _PacketInfo object
745
746 :param payload: packet payload
747
748 :returns: _PacketInfo object containing de-serialized data from payload
749
750 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200751 numbers = payload.split()
752 info = _PacketInfo()
753 info.index = int(numbers[0])
754 info.src = int(numbers[1])
755 info.dst = int(numbers[2])
Pavel Kotucek59dda062017-03-02 15:22:47 +0100756 info.ip = int(numbers[3])
757 info.proto = int(numbers[4])
Damjan Marionf56b77a2016-10-03 19:44:57 +0200758 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200759
Damjan Marionf56b77a2016-10-03 19:44:57 +0200760 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200761 """
762 Iterate over the packet info list stored in the testcase
763 Start iteration with first element if info is None
764 Continue based on index in info if info is specified
765
766 :param info: info or None
767 :returns: next info in list or None if no more infos
768 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769 if info is None:
770 next_index = 0
771 else:
772 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100773 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200774 return None
775 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100776 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200777
Klement Sekeraf62ae122016-10-11 11:47:09 +0200778 def get_next_packet_info_for_interface(self, src_index, info):
779 """
780 Search the packet info list for the next packet info with same source
781 interface index
782
783 :param src_index: source interface index to search for
784 :param info: packet info - where to start the search
785 :returns: packet info or None
786
787 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200788 while True:
789 info = self.get_next_packet_info(info)
790 if info is None:
791 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200792 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200793 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200794
Klement Sekeraf62ae122016-10-11 11:47:09 +0200795 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
796 """
797 Search the packet info list for the next packet info with same source
798 and destination interface indexes
799
800 :param src_index: source interface index to search for
801 :param dst_index: destination interface index to search for
802 :param info: packet info - where to start the search
803 :returns: packet info or None
804
805 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200806 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200807 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200808 if info is None:
809 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200810 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200811 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200812
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200813 def assert_equal(self, real_value, expected_value, name_or_class=None):
814 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100815 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200816 return
817 try:
818 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
819 msg = msg % (getdoc(name_or_class).strip(),
820 real_value, str(name_or_class(real_value)),
821 expected_value, str(name_or_class(expected_value)))
Klement Sekera13a83ef2018-03-21 12:35:51 +0100822 except Exception:
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200823 msg = "Invalid %s: %s does not match expected value %s" % (
824 name_or_class, real_value, expected_value)
825
826 self.assertEqual(real_value, expected_value, msg)
827
Klement Sekerab17dd962017-01-09 07:43:48 +0100828 def assert_in_range(self,
829 real_value,
830 expected_min,
831 expected_max,
832 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200833 if name is None:
834 msg = None
835 else:
836 msg = "Invalid %s: %s out of range <%s,%s>" % (
837 name, real_value, expected_min, expected_max)
838 self.assertTrue(expected_min <= real_value <= expected_max, msg)
839
Klement Sekerad81ae412018-05-16 10:52:54 +0200840 def assert_packet_checksums_valid(self, packet,
841 ignore_zero_udp_checksums=True):
Klement Sekera31da2e32018-06-24 22:49:55 +0200842 received = packet.__class__(str(packet))
843 self.logger.debug(
844 ppp("Verifying packet checksums for packet:", received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200845 udp_layers = ['UDP', 'UDPerror']
846 checksum_fields = ['cksum', 'chksum']
847 checksums = []
848 counter = 0
Klement Sekera31da2e32018-06-24 22:49:55 +0200849 temp = received.__class__(str(received))
Klement Sekerad81ae412018-05-16 10:52:54 +0200850 while True:
851 layer = temp.getlayer(counter)
852 if layer:
853 for cf in checksum_fields:
854 if hasattr(layer, cf):
855 if ignore_zero_udp_checksums and \
Paul Vinciguerra919efad2018-12-17 21:43:43 -0800856 0 == getattr(layer, cf) and \
857 layer.name in udp_layers:
Klement Sekerad81ae412018-05-16 10:52:54 +0200858 continue
859 delattr(layer, cf)
860 checksums.append((counter, cf))
861 else:
862 break
863 counter = counter + 1
Klement Sekera31da2e32018-06-24 22:49:55 +0200864 if 0 == len(checksums):
865 return
Klement Sekerad81ae412018-05-16 10:52:54 +0200866 temp = temp.__class__(str(temp))
867 for layer, cf in checksums:
Klement Sekera31da2e32018-06-24 22:49:55 +0200868 calc_sum = getattr(temp[layer], cf)
869 self.assert_equal(
870 getattr(received[layer], cf), calc_sum,
871 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
872 self.logger.debug(
873 "Checksum field `%s` on `%s` layer has correct value `%s`" %
874 (cf, temp[layer].name, calc_sum))
Klement Sekerad81ae412018-05-16 10:52:54 +0200875
876 def assert_checksum_valid(self, received_packet, layer,
877 field_name='chksum',
878 ignore_zero_checksum=False):
879 """ Check checksum of received packet on given layer """
880 received_packet_checksum = getattr(received_packet[layer], field_name)
881 if ignore_zero_checksum and 0 == received_packet_checksum:
882 return
883 recalculated = received_packet.__class__(str(received_packet))
884 delattr(recalculated[layer], field_name)
885 recalculated = recalculated.__class__(str(recalculated))
886 self.assert_equal(received_packet_checksum,
887 getattr(recalculated[layer], field_name),
888 "packet checksum on layer: %s" % layer)
889
890 def assert_ip_checksum_valid(self, received_packet,
891 ignore_zero_checksum=False):
892 self.assert_checksum_valid(received_packet, 'IP',
893 ignore_zero_checksum=ignore_zero_checksum)
894
895 def assert_tcp_checksum_valid(self, received_packet,
896 ignore_zero_checksum=False):
897 self.assert_checksum_valid(received_packet, 'TCP',
898 ignore_zero_checksum=ignore_zero_checksum)
899
900 def assert_udp_checksum_valid(self, received_packet,
901 ignore_zero_checksum=True):
902 self.assert_checksum_valid(received_packet, 'UDP',
903 ignore_zero_checksum=ignore_zero_checksum)
904
905 def assert_embedded_icmp_checksum_valid(self, received_packet):
906 if received_packet.haslayer(IPerror):
907 self.assert_checksum_valid(received_packet, 'IPerror')
908 if received_packet.haslayer(TCPerror):
909 self.assert_checksum_valid(received_packet, 'TCPerror')
910 if received_packet.haslayer(UDPerror):
911 self.assert_checksum_valid(received_packet, 'UDPerror',
912 ignore_zero_checksum=True)
913 if received_packet.haslayer(ICMPerror):
914 self.assert_checksum_valid(received_packet, 'ICMPerror')
915
916 def assert_icmp_checksum_valid(self, received_packet):
917 self.assert_checksum_valid(received_packet, 'ICMP')
918 self.assert_embedded_icmp_checksum_valid(received_packet)
919
920 def assert_icmpv6_checksum_valid(self, pkt):
921 if pkt.haslayer(ICMPv6DestUnreach):
922 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
923 self.assert_embedded_icmp_checksum_valid(pkt)
924 if pkt.haslayer(ICMPv6EchoRequest):
925 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
926 if pkt.haslayer(ICMPv6EchoReply):
927 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
928
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100929 def assert_packet_counter_equal(self, counter, expected_value):
Klement Sekera14d7e902018-12-10 13:46:09 +0100930 if counter.startswith("/"):
931 counter_value = self.statistics.get_counter(counter)
932 self.assert_equal(counter_value, expected_value,
933 "packet counter `%s'" % counter)
934 else:
935 counters = self.vapi.cli("sh errors").split('\n')
936 counter_value = -1
937 for i in range(1, len(counters) - 1):
938 results = counters[i].split()
939 if results[1] == counter:
940 counter_value = int(results[0])
941 break
Klement Sekeraf37c3ba2018-11-08 11:24:34 +0100942
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100943 @classmethod
944 def sleep(cls, timeout, remark=None):
945 if hasattr(cls, 'logger'):
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800946 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000947 before = time.time()
Klement Sekeraa57a9702017-02-02 06:58:07 +0100948 time.sleep(timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000949 after = time.time()
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800950 if hasattr(cls, 'logger') and after - before > 2 * timeout:
Klement Sekera60c12232017-07-18 10:33:06 +0200951 cls.logger.error("unexpected time.sleep() result - "
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800952 "slept for %es instead of ~%es!",
953 after - before, timeout)
Klement Sekera3cfa5582017-04-19 07:10:58 +0000954 if hasattr(cls, 'logger'):
955 cls.logger.debug(
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -0800956 "Finished sleep (%s) - slept %es (wanted %es)",
957 remark, after - before, timeout)
Klement Sekeraa57a9702017-02-02 06:58:07 +0100958
Neale Ranns947ea622018-06-07 23:48:20 -0700959 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
Neale Ranns52fae862018-01-08 04:41:42 -0800960 self.vapi.cli("clear trace")
961 intf.add_stream(pkts)
962 self.pg_enable_capture(self.pg_interfaces)
963 self.pg_start()
Neale Ranns947ea622018-06-07 23:48:20 -0700964 if not timeout:
965 timeout = 1
Neale Ranns52fae862018-01-08 04:41:42 -0800966 for i in self.pg_interfaces:
967 i.get_capture(0, timeout=timeout)
968 i.assert_nothing_captured(remark=remark)
969 timeout = 0.1
970
971 def send_and_expect(self, input, pkts, output):
972 self.vapi.cli("clear trace")
973 input.add_stream(pkts)
974 self.pg_enable_capture(self.pg_interfaces)
975 self.pg_start()
Neale Ranns93cc3ee2018-10-10 07:22:51 -0700976 if isinstance(object, (list,)):
977 rx = []
978 for o in output:
979 rx.append(output.get_capture(len(pkts)))
980 else:
981 rx = output.get_capture(len(pkts))
982 return rx
983
984 def send_and_expect_only(self, input, pkts, output, timeout=None):
985 self.vapi.cli("clear trace")
986 input.add_stream(pkts)
987 self.pg_enable_capture(self.pg_interfaces)
988 self.pg_start()
989 if isinstance(object, (list,)):
990 outputs = output
991 rx = []
992 for o in outputs:
993 rx.append(output.get_capture(len(pkts)))
994 else:
995 rx = output.get_capture(len(pkts))
996 outputs = [output]
997 if not timeout:
998 timeout = 1
999 for i in self.pg_interfaces:
1000 if i not in outputs:
1001 i.get_capture(0, timeout=timeout)
1002 i.assert_nothing_captured()
1003 timeout = 0.1
1004
Neale Ranns52fae862018-01-08 04:41:42 -08001005 return rx
1006
Damjan Marionf56b77a2016-10-03 19:44:57 +02001007
juraj.linkes184870a2018-07-16 14:22:01 +02001008def get_testcase_doc_name(test):
1009 return getdoc(test.__class__).splitlines()[0]
1010
1011
Ole Trøan5ba91592018-11-22 10:01:09 +00001012def get_test_description(descriptions, test):
1013 short_description = test.shortDescription()
1014 if descriptions and short_description:
1015 return short_description
1016 else:
1017 return str(test)
1018
1019
juraj.linkes40dd73b2018-09-21 13:55:16 +02001020class TestCaseInfo(object):
1021 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1022 self.logger = logger
1023 self.tempdir = tempdir
1024 self.vpp_pid = vpp_pid
1025 self.vpp_bin_path = vpp_bin_path
1026 self.core_crash_test = None
Klement Sekera87134932017-03-07 11:39:27 +01001027
1028
Damjan Marionf56b77a2016-10-03 19:44:57 +02001029class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001030 """
1031 @property result_string
1032 String variable to store the test case result string.
1033 @property errors
1034 List variable containing 2-tuples of TestCase instances and strings
1035 holding formatted tracebacks. Each tuple represents a test which
1036 raised an unexpected exception.
1037 @property failures
1038 List variable containing 2-tuples of TestCase instances and strings
1039 holding formatted tracebacks. Each tuple represents a test where
1040 a failure was explicitly signalled using the TestCase.assert*()
1041 methods.
1042 """
1043
juraj.linkes40dd73b2018-09-21 13:55:16 +02001044 failed_test_cases_info = set()
1045 core_crash_test_cases_info = set()
1046 current_test_case_info = None
1047
juraj.linkesabec0122018-11-16 17:28:56 +01001048 def __init__(self, stream, descriptions, verbosity, runner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001049 """
Klement Sekerada505f62017-01-04 12:58:53 +01001050 :param stream File descriptor to store where to report test results.
1051 Set to the standard error stream by default.
1052 :param descriptions Boolean variable to store information if to use
1053 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001054 :param verbosity Integer variable to store required verbosity level.
1055 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001056 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
1057 self.stream = stream
1058 self.descriptions = descriptions
1059 self.verbosity = verbosity
1060 self.result_string = None
juraj.linkesabec0122018-11-16 17:28:56 +01001061 self.runner = runner
Damjan Marionf56b77a2016-10-03 19:44:57 +02001062
Damjan Marionf56b77a2016-10-03 19:44:57 +02001063 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001064 """
1065 Record a test succeeded result
1066
1067 :param test:
1068
1069 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001070 if self.current_test_case_info:
1071 self.current_test_case_info.logger.debug(
1072 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1073 test._testMethodName,
1074 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +02001075 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +02001076 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001077
juraj.linkescae64f82018-09-19 15:01:47 +02001078 self.send_result_through_pipe(test, PASS)
1079
Klement Sekeraf62ae122016-10-11 11:47:09 +02001080 def addSkip(self, test, reason):
1081 """
1082 Record a test skipped.
1083
1084 :param test:
1085 :param reason:
1086
1087 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001088 if self.current_test_case_info:
1089 self.current_test_case_info.logger.debug(
1090 "--- addSkip() %s.%s(%s) called, reason is %s" %
1091 (test.__class__.__name__, test._testMethodName,
1092 test._testMethodDoc, reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001093 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +02001094 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +02001095
juraj.linkescae64f82018-09-19 15:01:47 +02001096 self.send_result_through_pipe(test, SKIP)
1097
juraj.linkes40dd73b2018-09-21 13:55:16 +02001098 def symlink_failed(self):
1099 if self.current_test_case_info:
Klement Sekeraf413bef2017-08-15 07:09:02 +02001100 try:
Klement Sekerab8c72a42018-11-08 11:21:39 +01001101 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +02001102 link_path = os.path.join(
1103 failed_dir,
1104 '%s-FAILED' %
1105 os.path.basename(self.current_test_case_info.tempdir))
1106 if self.current_test_case_info.logger:
1107 self.current_test_case_info.logger.debug(
1108 "creating a link to the failed test")
1109 self.current_test_case_info.logger.debug(
1110 "os.symlink(%s, %s)" %
1111 (self.current_test_case_info.tempdir, link_path))
juraj.linkes184870a2018-07-16 14:22:01 +02001112 if os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +02001113 if self.current_test_case_info.logger:
1114 self.current_test_case_info.logger.debug(
1115 'symlink already exists')
juraj.linkes184870a2018-07-16 14:22:01 +02001116 else:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001117 os.symlink(self.current_test_case_info.tempdir, link_path)
juraj.linkes184870a2018-07-16 14:22:01 +02001118
Klement Sekeraf413bef2017-08-15 07:09:02 +02001119 except Exception as e:
juraj.linkes40dd73b2018-09-21 13:55:16 +02001120 if self.current_test_case_info.logger:
1121 self.current_test_case_info.logger.error(e)
Klement Sekeraf413bef2017-08-15 07:09:02 +02001122
juraj.linkescae64f82018-09-19 15:01:47 +02001123 def send_result_through_pipe(self, test, result):
1124 if hasattr(self, 'test_framework_result_pipe'):
1125 pipe = self.test_framework_result_pipe
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001126 if pipe:
juraj.linkescae64f82018-09-19 15:01:47 +02001127 pipe.send((test.id(), result))
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001128
juraj.linkes40dd73b2018-09-21 13:55:16 +02001129 def log_error(self, test, err, fn_name):
1130 if self.current_test_case_info:
1131 if isinstance(test, unittest.suite._ErrorHolder):
1132 test_name = test.description
1133 else:
1134 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1135 test._testMethodName,
1136 test._testMethodDoc)
1137 self.current_test_case_info.logger.debug(
1138 "--- %s() %s called, err is %s" %
1139 (fn_name, test_name, err))
1140 self.current_test_case_info.logger.debug(
1141 "formatted exception is:\n%s" %
1142 "".join(format_exception(*err)))
1143
1144 def add_error(self, test, err, unittest_fn, error_type):
1145 if error_type == FAIL:
1146 self.log_error(test, err, 'addFailure')
1147 error_type_str = colorize("FAIL", RED)
1148 elif error_type == ERROR:
1149 self.log_error(test, err, 'addError')
1150 error_type_str = colorize("ERROR", RED)
1151 else:
1152 raise Exception('Error type %s cannot be used to record an '
1153 'error or a failure' % error_type)
1154
1155 unittest_fn(self, test, err)
1156 if self.current_test_case_info:
1157 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1158 (error_type_str,
1159 self.current_test_case_info.tempdir)
1160 self.symlink_failed()
1161 self.failed_test_cases_info.add(self.current_test_case_info)
1162 if is_core_present(self.current_test_case_info.tempdir):
1163 if not self.current_test_case_info.core_crash_test:
1164 if isinstance(test, unittest.suite._ErrorHolder):
1165 test_name = str(test)
1166 else:
1167 test_name = "'{}' ({})".format(
1168 get_testcase_doc_name(test), test.id())
1169 self.current_test_case_info.core_crash_test = test_name
1170 self.core_crash_test_cases_info.add(
1171 self.current_test_case_info)
1172 else:
1173 self.result_string = '%s [no temp dir]' % error_type_str
1174
1175 self.send_result_through_pipe(test, error_type)
1176
Damjan Marionf56b77a2016-10-03 19:44:57 +02001177 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001178 """
1179 Record a test failed result
1180
1181 :param test:
1182 :param err: error message
1183
1184 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001185 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
juraj.linkescae64f82018-09-19 15:01:47 +02001186
Damjan Marionf56b77a2016-10-03 19:44:57 +02001187 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001188 """
1189 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +02001190
Klement Sekeraf62ae122016-10-11 11:47:09 +02001191 :param test:
1192 :param err: error message
1193
1194 """
juraj.linkes40dd73b2018-09-21 13:55:16 +02001195 self.add_error(test, err, unittest.TestResult.addError, ERROR)
juraj.linkescae64f82018-09-19 15:01:47 +02001196
Damjan Marionf56b77a2016-10-03 19:44:57 +02001197 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001198 """
1199 Get test description
1200
1201 :param test:
1202 :returns: test description
1203
1204 """
Ole Trøan5ba91592018-11-22 10:01:09 +00001205 return get_test_description(self.descriptions, test)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001206
Damjan Marionf56b77a2016-10-03 19:44:57 +02001207 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001208 """
1209 Start a test
1210
1211 :param test:
1212
1213 """
Paul Vinciguerra86ebba62018-11-21 09:28:32 -08001214 test.print_header()
juraj.linkes40dd73b2018-09-21 13:55:16 +02001215
Damjan Marionf56b77a2016-10-03 19:44:57 +02001216 unittest.TestResult.startTest(self, test)
1217 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001218 self.stream.writeln(
1219 "Starting " + self.getDescription(test) + " ...")
1220 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001221
Damjan Marionf56b77a2016-10-03 19:44:57 +02001222 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001223 """
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001224 Called when the given test has been run
Klement Sekeraf62ae122016-10-11 11:47:09 +02001225
1226 :param test:
1227
1228 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001229 unittest.TestResult.stopTest(self, test)
1230 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001231 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +01001232 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001233 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +02001234 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001235 else:
Klement Sekera52e84f32017-01-13 07:25:25 +01001236 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +01001237 self.result_string))
juraj.linkescae64f82018-09-19 15:01:47 +02001238
1239 self.send_result_through_pipe(test, TEST_RUN)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001240
Damjan Marionf56b77a2016-10-03 19:44:57 +02001241 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001242 """
1243 Print errors from running the test case
1244 """
juraj.linkesabec0122018-11-16 17:28:56 +01001245 if len(self.errors) > 0 or len(self.failures) > 0:
1246 self.stream.writeln()
1247 self.printErrorList('ERROR', self.errors)
1248 self.printErrorList('FAIL', self.failures)
1249
1250 # ^^ that is the last output from unittest before summary
1251 if not self.runner.print_summary:
1252 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1253 self.stream = devnull
1254 self.runner.stream = devnull
Damjan Marionf56b77a2016-10-03 19:44:57 +02001255
Damjan Marionf56b77a2016-10-03 19:44:57 +02001256 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001257 """
1258 Print error list to the output stream together with error type
1259 and test case description.
1260
1261 :param flavour: error type
1262 :param errors: iterable errors
1263
1264 """
Damjan Marionf56b77a2016-10-03 19:44:57 +02001265 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +02001266 self.stream.writeln(double_line_delim)
1267 self.stream.writeln("%s: %s" %
1268 (flavour, self.getDescription(test)))
1269 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001270 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +02001271
1272
Damjan Marionf56b77a2016-10-03 19:44:57 +02001273class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001274 """
Klement Sekera104543f2017-02-03 07:29:43 +01001275 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +02001276 """
Paul Vinciguerra6919b0d2018-12-09 15:37:04 -08001277
Klement Sekeraf62ae122016-10-11 11:47:09 +02001278 @property
1279 def resultclass(self):
1280 """Class maintaining the results of the tests"""
1281 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +02001282
juraj.linkes184870a2018-07-16 14:22:01 +02001283 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
juraj.linkescae64f82018-09-19 15:01:47 +02001284 result_pipe=None, failfast=False, buffer=False,
juraj.linkesabec0122018-11-16 17:28:56 +01001285 resultclass=None, print_summary=True):
Klement Sekera7a161da2017-01-17 13:42:48 +01001286 # ignore stream setting here, use hard-coded stdout to be in sync
1287 # with prints from VppTestCase methods ...
1288 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1289 verbosity, failfast, buffer,
1290 resultclass)
juraj.linkesccfead62018-11-21 13:20:43 +01001291 KeepAliveReporter.pipe = keep_alive_pipe
Klement Sekera104543f2017-02-03 07:29:43 +01001292
juraj.linkesabec0122018-11-16 17:28:56 +01001293 self.orig_stream = self.stream
1294 self.resultclass.test_framework_result_pipe = result_pipe
1295
1296 self.print_summary = print_summary
1297
1298 def _makeResult(self):
1299 return self.resultclass(self.stream,
1300 self.descriptions,
1301 self.verbosity,
1302 self)
juraj.linkes5e2c54d2018-08-30 10:51:45 +02001303
Damjan Marionf56b77a2016-10-03 19:44:57 +02001304 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +02001305 """
1306 Run the tests
1307
1308 :param test:
1309
1310 """
Klement Sekera3658adc2017-06-07 08:19:47 +02001311 faulthandler.enable() # emit stack trace to stderr if killed by signal
juraj.linkes184870a2018-07-16 14:22:01 +02001312
1313 result = super(VppTestRunner, self).run(test)
juraj.linkesabec0122018-11-16 17:28:56 +01001314 if not self.print_summary:
1315 self.stream = self.orig_stream
1316 result.stream = self.orig_stream
juraj.linkes184870a2018-07-16 14:22:01 +02001317 return result
Neale Ranns812ed392017-10-16 04:20:13 -07001318
1319
1320class Worker(Thread):
Dave Wallace42996c02018-02-26 14:40:13 -05001321 def __init__(self, args, logger, env={}):
Neale Ranns812ed392017-10-16 04:20:13 -07001322 self.logger = logger
1323 self.args = args
1324 self.result = None
Dave Wallace42996c02018-02-26 14:40:13 -05001325 self.env = copy.deepcopy(env)
Neale Ranns812ed392017-10-16 04:20:13 -07001326 super(Worker, self).__init__()
1327
1328 def run(self):
1329 executable = self.args[0]
1330 self.logger.debug("Running executable w/args `%s'" % self.args)
1331 env = os.environ.copy()
Dave Wallacecfcf2f42018-02-16 18:31:56 -05001332 env.update(self.env)
Neale Ranns812ed392017-10-16 04:20:13 -07001333 env["CK_LOG_FILE_NAME"] = "-"
1334 self.process = subprocess.Popen(
1335 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1336 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1337 out, err = self.process.communicate()
1338 self.logger.debug("Finished running `%s'" % executable)
1339 self.logger.info("Return code is `%s'" % self.process.returncode)
1340 self.logger.info(single_line_delim)
1341 self.logger.info("Executable `%s' wrote to stdout:" % executable)
1342 self.logger.info(single_line_delim)
1343 self.logger.info(out)
1344 self.logger.info(single_line_delim)
1345 self.logger.info("Executable `%s' wrote to stderr:" % executable)
1346 self.logger.info(single_line_delim)
Klement Sekerade0203e2018-02-22 19:21:27 +01001347 self.logger.info(err)
Neale Ranns812ed392017-10-16 04:20:13 -07001348 self.logger.info(single_line_delim)
1349 self.result = self.process.returncode