blob: a0284e37c289fefbe0f5f6c31fa8a40a289a1019 [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
5import sys
6import os
7import select
Damjan Marionf56b77a2016-10-03 19:44:57 +02008import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +02009import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +020010import time
Klement Sekeraf62ae122016-10-11 11:47:09 +020011import resource
Klement Sekerae4504c62016-12-08 10:16:41 +010012from collections import deque
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010013from threading import Thread, Event
Damjan Marionf56b77a2016-10-03 19:44:57 +020014from inspect import getdoc
Klement Sekerab91017a2017-02-09 06:04:36 +010015from traceback import format_exception
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010016from logging import FileHandler, DEBUG, Formatter
17from scapy.packet import Raw
Klement Sekera277b89c2016-10-28 13:20:27 +020018from hook import StepHook, PollHook
Klement Sekeraf62ae122016-10-11 11:47:09 +020019from vpp_pg_interface import VppPGInterface
Klement Sekeradab231a2016-12-21 08:50:14 +010020from vpp_sub_interface import VppSubInterface
Matej Klotton0178d522016-11-04 11:11:44 +010021from vpp_lo_interface import VppLoInterface
Klement Sekeraf62ae122016-10-11 11:47:09 +020022from vpp_papi_provider import VppPapiProvider
Klement Sekera277b89c2016-10-28 13:20:27 +020023from log import *
Klement Sekera10db26f2017-01-11 08:16:53 +010024from vpp_object import VppObjectRegistry
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010025if os.name == 'posix' and sys.version_info[0] < 3:
26 # using subprocess32 is recommended by python official documentation
27 # @ https://docs.python.org/2/library/subprocess.html
28 import subprocess32 as subprocess
29else:
30 import subprocess
Klement Sekeraf62ae122016-10-11 11:47:09 +020031
32"""
33 Test framework module.
34
35 The module provides a set of tools for constructing and running tests and
36 representing the results.
37"""
38
Klement Sekeraf62ae122016-10-11 11:47:09 +020039
Damjan Marionf56b77a2016-10-03 19:44:57 +020040class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020041 """Private class to create packet info object.
42
43 Help process information about the next packet.
44 Set variables to default values.
Klement Sekeraf62ae122016-10-11 11:47:09 +020045 """
Matej Klotton86d87c42016-11-11 11:38:55 +010046 #: Store the index of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020047 index = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010048 #: Store the index of the source packet generator interface of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020049 src = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010050 #: Store the index of the destination packet generator interface
51 #: of the packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020052 dst = -1
Matej Klotton86d87c42016-11-11 11:38:55 +010053 #: Store the copy of the former packet.
Damjan Marionf56b77a2016-10-03 19:44:57 +020054 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020055
Matej Klotton16a14cd2016-12-07 15:09:13 +010056 def __eq__(self, other):
57 index = self.index == other.index
58 src = self.src == other.src
59 dst = self.dst == other.dst
60 data = self.data == other.data
61 return index and src and dst and data
62
Klement Sekeraf62ae122016-10-11 11:47:09 +020063
Klement Sekeraacb9b8e2017-02-14 02:55:31 +010064def pump_output(testclass):
65 """ pump output from vpp stdout/stderr to proper queues """
66 while not testclass.pump_thread_stop_flag.wait(0):
67 readable = select.select([testclass.vpp.stdout.fileno(),
68 testclass.vpp.stderr.fileno(),
69 testclass.pump_thread_wakeup_pipe[0]],
70 [], [])[0]
71 if testclass.vpp.stdout.fileno() in readable:
72 read = os.read(testclass.vpp.stdout.fileno(), 1024)
73 testclass.vpp_stdout_deque.append(read)
74 if testclass.vpp.stderr.fileno() in readable:
75 read = os.read(testclass.vpp.stderr.fileno(), 1024)
76 testclass.vpp_stderr_deque.append(read)
77 # ignoring the dummy pipe here intentionally - the flag will take care
78 # of properly terminating the loop
Klement Sekera277b89c2016-10-28 13:20:27 +020079
80
Klement Sekera87134932017-03-07 11:39:27 +010081def running_extended_tests():
82 try:
83 s = os.getenv("EXTENDED_TESTS")
84 return True if s.lower() in ("y", "yes", "1") else False
85 except:
86 return False
87 return False
88
89
Damjan Marionf56b77a2016-10-03 19:44:57 +020090class VppTestCase(unittest.TestCase):
Matej Klotton86d87c42016-11-11 11:38:55 +010091 """This subclass is a base class for VPP test cases that are implemented as
92 classes. It provides methods to create and run test case.
Klement Sekeraf62ae122016-10-11 11:47:09 +020093 """
94
95 @property
96 def packet_infos(self):
97 """List of packet infos"""
98 return self._packet_infos
99
Klement Sekeradab231a2016-12-21 08:50:14 +0100100 @classmethod
101 def get_packet_count_for_if_idx(cls, dst_if_index):
102 """Get the number of packet info for specified destination if index"""
103 if dst_if_index in cls._packet_count_for_dst_if_idx:
104 return cls._packet_count_for_dst_if_idx[dst_if_index]
105 else:
106 return 0
Klement Sekeraf62ae122016-10-11 11:47:09 +0200107
108 @classmethod
109 def instance(cls):
110 """Return the instance of this testcase"""
111 return cls.test_instance
112
Damjan Marionf56b77a2016-10-03 19:44:57 +0200113 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +0200114 def set_debug_flags(cls, d):
115 cls.debug_core = False
116 cls.debug_gdb = False
117 cls.debug_gdbserver = False
118 if d is None:
119 return
120 dl = d.lower()
121 if dl == "core":
Klement Sekera277b89c2016-10-28 13:20:27 +0200122 cls.debug_core = True
123 elif dl == "gdb":
124 cls.debug_gdb = True
125 elif dl == "gdbserver":
126 cls.debug_gdbserver = True
127 else:
128 raise Exception("Unrecognized DEBUG option: '%s'" % d)
129
130 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200131 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200132 """ Set-up the test case class based on environment variables """
133 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200134 s = os.getenv("STEP")
135 cls.step = True if s.lower() in ("y", "yes", "1") else False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200136 except:
Klement Sekera277b89c2016-10-28 13:20:27 +0200137 cls.step = False
138 try:
139 d = os.getenv("DEBUG")
140 except:
141 d = None
142 cls.set_debug_flags(d)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200143 cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp")
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100144 cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera01bbbe92016-11-02 09:25:05 +0100145 debug_cli = ""
146 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
147 debug_cli = "cli-listen localhost:5002"
Klement Sekera80a7f0a2017-03-02 11:27:11 +0100148 coredump_size = None
149 try:
150 size = os.getenv("COREDUMP_SIZE")
151 if size is not None:
152 coredump_size = "coredump-size %s" % size
153 except:
154 pass
155 if coredump_size is None:
156 coredump_size = "coredump-size unlimited"
157 cls.vpp_cmdline = [cls.vpp_bin, "unix",
158 "{", "nodaemon", debug_cli, coredump_size, "}",
Dave Wallace90c55722017-02-16 11:25:26 -0500159 "api-trace", "{", "on", "}",
Damjan Marion374e2c52017-03-09 20:38:15 +0100160 "api-segment", "{", "prefix", cls.shm_prefix, "}",
161 "plugins", "{", "plugin", "dpdk_plugin.so", "{",
162 "disable", "}", "}"]
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100163 if cls.plugin_path is not None:
164 cls.vpp_cmdline.extend(["plugin_path", cls.plugin_path])
Klement Sekera277b89c2016-10-28 13:20:27 +0200165 cls.logger.info("vpp_cmdline: %s" % cls.vpp_cmdline)
166
167 @classmethod
168 def wait_for_enter(cls):
169 if cls.debug_gdbserver:
170 print(double_line_delim)
171 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
172 elif cls.debug_gdb:
173 print(double_line_delim)
174 print("Spawned VPP with PID: %d" % cls.vpp.pid)
175 else:
176 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
177 return
178 print(single_line_delim)
179 print("You can debug the VPP using e.g.:")
180 if cls.debug_gdbserver:
181 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
182 print("Now is the time to attach a gdb by running the above "
183 "command, set up breakpoints etc. and then resume VPP from "
184 "within gdb by issuing the 'continue' command")
185 elif cls.debug_gdb:
186 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
187 print("Now is the time to attach a gdb by running the above "
188 "command and set up breakpoints etc.")
189 print(single_line_delim)
190 raw_input("Press ENTER to continue running the testcase...")
191
192 @classmethod
193 def run_vpp(cls):
194 cmdline = cls.vpp_cmdline
195
196 if cls.debug_gdbserver:
Klement Sekera931be3a2016-11-03 05:36:01 +0100197 gdbserver = '/usr/bin/gdbserver'
198 if not os.path.isfile(gdbserver) or \
199 not os.access(gdbserver, os.X_OK):
200 raise Exception("gdbserver binary '%s' does not exist or is "
201 "not executable" % gdbserver)
202
203 cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
Klement Sekera277b89c2016-10-28 13:20:27 +0200204 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
205
Klement Sekera931be3a2016-11-03 05:36:01 +0100206 try:
207 cls.vpp = subprocess.Popen(cmdline,
208 stdout=subprocess.PIPE,
209 stderr=subprocess.PIPE,
210 bufsize=1)
211 except Exception as e:
212 cls.logger.critical("Couldn't start vpp: %s" % e)
213 raise
214
Klement Sekera277b89c2016-10-28 13:20:27 +0200215 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100216
Damjan Marionf56b77a2016-10-03 19:44:57 +0200217 @classmethod
218 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200219 """
220 Perform class setup before running the testcase
221 Remove shared memory files, start vpp and connect the vpp-api
222 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100223 gc.collect() # run garbage collection first
Klement Sekera277b89c2016-10-28 13:20:27 +0200224 cls.logger = getLogger(cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200225 cls.tempdir = tempfile.mkdtemp(
226 prefix='vpp-unittest-' + cls.__name__ + '-')
Klement Sekera6c7440c2016-12-23 09:16:39 +0100227 file_handler = FileHandler("%s/log.txt" % cls.tempdir)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100228 file_handler.setFormatter(
229 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
230 datefmt="%H:%M:%S"))
Klement Sekera6c7440c2016-12-23 09:16:39 +0100231 file_handler.setLevel(DEBUG)
232 cls.logger.addHandler(file_handler)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200233 cls.shm_prefix = cls.tempdir.split("/")[-1]
234 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200235 cls.logger.info("Temporary dir is %s, shm prefix is %s",
236 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200237 cls.setUpConstants()
Klement Sekeradab231a2016-12-21 08:50:14 +0100238 cls.reset_packet_infos()
Klement Sekera9225dee2016-12-12 08:36:58 +0100239 cls._captures = []
240 cls._zombie_captures = []
Klement Sekeraf62ae122016-10-11 11:47:09 +0200241 cls.verbose = 0
Klement Sekera085f5c02016-11-24 01:59:16 +0100242 cls.vpp_dead = False
Klement Sekera10db26f2017-01-11 08:16:53 +0100243 cls.registry = VppObjectRegistry()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200244 # need to catch exceptions here because if we raise, then the cleanup
245 # doesn't get called and we might end with a zombie vpp
246 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200247 cls.run_vpp()
Klement Sekerae4504c62016-12-08 10:16:41 +0100248 cls.vpp_stdout_deque = deque()
Klement Sekerae4504c62016-12-08 10:16:41 +0100249 cls.vpp_stderr_deque = deque()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100250 cls.pump_thread_stop_flag = Event()
251 cls.pump_thread_wakeup_pipe = os.pipe()
252 cls.pump_thread = Thread(target=pump_output, args=(cls,))
Klement Sekeraaeeac3b2017-02-14 07:11:52 +0100253 cls.pump_thread.daemon = True
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100254 cls.pump_thread.start()
Klement Sekera7bb873a2016-11-18 07:38:42 +0100255 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls)
Klement Sekera085f5c02016-11-24 01:59:16 +0100256 if cls.step:
257 hook = StepHook(cls)
258 else:
259 hook = PollHook(cls)
260 cls.vapi.register_hook(hook)
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100261 cls.sleep(0.1, "after vpp startup, before initial poll")
Klement Sekera085f5c02016-11-24 01:59:16 +0100262 hook.poll_vpp()
263 try:
264 cls.vapi.connect()
265 except:
266 if cls.debug_gdbserver:
267 print(colorize("You're running VPP inside gdbserver but "
268 "VPP-API connection failed, did you forget "
269 "to 'continue' VPP from within gdb?", RED))
270 raise
Klement Sekeraf62ae122016-10-11 11:47:09 +0200271 except:
Klement Sekera0529a742016-12-02 07:05:24 +0100272 t, v, tb = sys.exc_info()
Klement Sekera085f5c02016-11-24 01:59:16 +0100273 try:
274 cls.quit()
275 except:
276 pass
Klement Sekera0529a742016-12-02 07:05:24 +0100277 raise t, v, tb
Damjan Marionf56b77a2016-10-03 19:44:57 +0200278
Damjan Marionf56b77a2016-10-03 19:44:57 +0200279 @classmethod
280 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200281 """
282 Disconnect vpp-api, kill vpp and cleanup shared memory files
283 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200284 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
285 cls.vpp.poll()
286 if cls.vpp.returncode is None:
287 print(double_line_delim)
288 print("VPP or GDB server is still running")
289 print(single_line_delim)
Klement Sekerada505f62017-01-04 12:58:53 +0100290 raw_input("When done debugging, press ENTER to kill the "
291 "process and finish running the testcase...")
Klement Sekera277b89c2016-10-28 13:20:27 +0200292
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100293 os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
294 cls.pump_thread_stop_flag.set()
295 if hasattr(cls, 'pump_thread'):
296 cls.logger.debug("Waiting for pump thread to stop")
297 cls.pump_thread.join()
298 if hasattr(cls, 'vpp_stderr_reader_thread'):
299 cls.logger.debug("Waiting for stdderr pump to stop")
300 cls.vpp_stderr_reader_thread.join()
301
Klement Sekeraf62ae122016-10-11 11:47:09 +0200302 if hasattr(cls, 'vpp'):
Klement Sekera0529a742016-12-02 07:05:24 +0100303 if hasattr(cls, 'vapi'):
304 cls.vapi.disconnect()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100305 del cls.vapi
Klement Sekeraf62ae122016-10-11 11:47:09 +0200306 cls.vpp.poll()
307 if cls.vpp.returncode is None:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100308 cls.logger.debug("Sending TERM to vpp")
Klement Sekeraf62ae122016-10-11 11:47:09 +0200309 cls.vpp.terminate()
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100310 cls.logger.debug("Waiting for vpp to die")
311 cls.vpp.communicate()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200312 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200313
Klement Sekerae4504c62016-12-08 10:16:41 +0100314 if hasattr(cls, 'vpp_stdout_deque'):
Klement Sekera277b89c2016-10-28 13:20:27 +0200315 cls.logger.info(single_line_delim)
316 cls.logger.info('VPP output to stdout while running %s:',
317 cls.__name__)
318 cls.logger.info(single_line_delim)
319 f = open(cls.tempdir + '/vpp_stdout.txt', 'w')
Klement Sekerae4504c62016-12-08 10:16:41 +0100320 vpp_output = "".join(cls.vpp_stdout_deque)
321 f.write(vpp_output)
322 cls.logger.info('\n%s', vpp_output)
323 cls.logger.info(single_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200324
Klement Sekerae4504c62016-12-08 10:16:41 +0100325 if hasattr(cls, 'vpp_stderr_deque'):
Klement Sekera277b89c2016-10-28 13:20:27 +0200326 cls.logger.info(single_line_delim)
327 cls.logger.info('VPP output to stderr while running %s:',
328 cls.__name__)
329 cls.logger.info(single_line_delim)
330 f = open(cls.tempdir + '/vpp_stderr.txt', 'w')
Klement Sekerae4504c62016-12-08 10:16:41 +0100331 vpp_output = "".join(cls.vpp_stderr_deque)
332 f.write(vpp_output)
333 cls.logger.info('\n%s', vpp_output)
Klement Sekera277b89c2016-10-28 13:20:27 +0200334 cls.logger.info(single_line_delim)
335
Damjan Marionf56b77a2016-10-03 19:44:57 +0200336 @classmethod
337 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200338 """ Perform final cleanup after running all tests in this test-case """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200339 cls.quit()
340
Damjan Marionf56b77a2016-10-03 19:44:57 +0200341 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200342 """ Show various debug prints after each test """
Klement Sekerab91017a2017-02-09 06:04:36 +0100343 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
344 (self.__class__.__name__, self._testMethodName,
345 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200346 if not self.vpp_dead:
Jan49c0fca2016-10-26 15:44:27 +0200347 self.logger.debug(self.vapi.cli("show trace"))
Neale Ranns177bbdc2016-11-15 09:46:51 +0000348 self.logger.info(self.vapi.ppcli("show int"))
Jan49c0fca2016-10-26 15:44:27 +0200349 self.logger.info(self.vapi.ppcli("show hardware"))
350 self.logger.info(self.vapi.ppcli("show error"))
351 self.logger.info(self.vapi.ppcli("show run"))
Klement Sekera10db26f2017-01-11 08:16:53 +0100352 self.registry.remove_vpp_config(self.logger)
Dave Wallace90c55722017-02-16 11:25:26 -0500353 # Save/Dump VPP api trace log
354 api_trace = "vpp_api_trace.%s.log" % self._testMethodName
355 tmp_api_trace = "/tmp/%s" % api_trace
356 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
357 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
358 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
359 vpp_api_trace_log))
360 os.rename(tmp_api_trace, vpp_api_trace_log)
361 self.logger.info(self.vapi.ppcli("api trace dump %s" %
362 vpp_api_trace_log))
Klement Sekera1b686402017-03-02 11:29:19 +0100363 else:
364 self.registry.unregister_all(self.logger)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200365
Damjan Marionf56b77a2016-10-03 19:44:57 +0200366 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200367 """ Clear trace before running each test"""
Klement Sekerab91017a2017-02-09 06:04:36 +0100368 self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
369 (self.__class__.__name__, self._testMethodName,
370 self._testMethodDoc))
Klement Sekera0c1519b2016-12-08 05:03:32 +0100371 if self.vpp_dead:
372 raise Exception("VPP is dead when setting up the test")
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100373 self.sleep(.1, "during setUp")
Klement Sekerae4504c62016-12-08 10:16:41 +0100374 self.vpp_stdout_deque.append(
375 "--- test setUp() for %s.%s(%s) starts here ---\n" %
376 (self.__class__.__name__, self._testMethodName,
377 self._testMethodDoc))
378 self.vpp_stderr_deque.append(
379 "--- test setUp() for %s.%s(%s) starts here ---\n" %
380 (self.__class__.__name__, self._testMethodName,
381 self._testMethodDoc))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200382 self.vapi.cli("clear trace")
383 # store the test instance inside the test class - so that objects
384 # holding the class can access instance methods (like assertEqual)
385 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200386
Damjan Marionf56b77a2016-10-03 19:44:57 +0200387 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200388 def pg_enable_capture(cls, interfaces):
389 """
390 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200391
Klement Sekeraf62ae122016-10-11 11:47:09 +0200392 :param interfaces: iterable interface indexes
Damjan Marionf56b77a2016-10-03 19:44:57 +0200393
Klement Sekeraf62ae122016-10-11 11:47:09 +0200394 """
395 for i in interfaces:
396 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200397
Damjan Marionf56b77a2016-10-03 19:44:57 +0200398 @classmethod
Klement Sekera9225dee2016-12-12 08:36:58 +0100399 def register_capture(cls, cap_name):
400 """ Register a capture in the testclass """
401 # add to the list of captures with current timestamp
402 cls._captures.append((time.time(), cap_name))
403 # filter out from zombies
404 cls._zombie_captures = [(stamp, name)
405 for (stamp, name) in cls._zombie_captures
406 if name != cap_name]
407
408 @classmethod
409 def pg_start(cls):
410 """ Remove any zombie captures and enable the packet generator """
411 # how long before capture is allowed to be deleted - otherwise vpp
412 # crashes - 100ms seems enough (this shouldn't be needed at all)
413 capture_ttl = 0.1
414 now = time.time()
415 for stamp, cap_name in cls._zombie_captures:
416 wait = stamp + capture_ttl - now
417 if wait > 0:
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100418 cls.sleep(wait, "before deleting capture %s" % cap_name)
Klement Sekera9225dee2016-12-12 08:36:58 +0100419 now = time.time()
420 cls.logger.debug("Removing zombie capture %s" % cap_name)
421 cls.vapi.cli('packet-generator delete %s' % cap_name)
422
Klement Sekeraf62ae122016-10-11 11:47:09 +0200423 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
424 cls.vapi.cli('packet-generator enable')
Klement Sekera9225dee2016-12-12 08:36:58 +0100425 cls._zombie_captures = cls._captures
426 cls._captures = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200427
Damjan Marionf56b77a2016-10-03 19:44:57 +0200428 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200429 def create_pg_interfaces(cls, interfaces):
430 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100431 Create packet-generator interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200432
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100433 :param interfaces: iterable indexes of the interfaces.
434 :returns: List of created interfaces.
Damjan Marionf56b77a2016-10-03 19:44:57 +0200435
Klement Sekeraf62ae122016-10-11 11:47:09 +0200436 """
437 result = []
438 for i in interfaces:
439 intf = VppPGInterface(cls, i)
440 setattr(cls, intf.name, intf)
441 result.append(intf)
442 cls.pg_interfaces = result
443 return result
444
Matej Klotton0178d522016-11-04 11:11:44 +0100445 @classmethod
446 def create_loopback_interfaces(cls, interfaces):
447 """
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100448 Create loopback interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100449
Matej Klotton8d8a1da2016-12-22 11:06:56 +0100450 :param interfaces: iterable indexes of the interfaces.
451 :returns: List of created interfaces.
Matej Klotton0178d522016-11-04 11:11:44 +0100452 """
453 result = []
454 for i in interfaces:
455 intf = VppLoInterface(cls, i)
456 setattr(cls, intf.name, intf)
457 result.append(intf)
458 cls.lo_interfaces = result
459 return result
460
Damjan Marionf56b77a2016-10-03 19:44:57 +0200461 @staticmethod
462 def extend_packet(packet, size):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200463 """
464 Extend packet to given size by padding with spaces
465 NOTE: Currently works only when Raw layer is present.
466
467 :param packet: packet
468 :param size: target size
469
470 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200471 packet_len = len(packet) + 4
472 extend = size - packet_len
473 if extend > 0:
474 packet[Raw].load += ' ' * extend
Damjan Marionf56b77a2016-10-03 19:44:57 +0200475
Klement Sekeradab231a2016-12-21 08:50:14 +0100476 @classmethod
477 def reset_packet_infos(cls):
478 """ Reset the list of packet info objects and packet counts to zero """
479 cls._packet_infos = {}
480 cls._packet_count_for_dst_if_idx = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200481
Klement Sekeradab231a2016-12-21 08:50:14 +0100482 @classmethod
483 def create_packet_info(cls, src_if, dst_if):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200484 """
485 Create packet info object containing the source and destination indexes
486 and add it to the testcase's packet info list
487
Klement Sekeradab231a2016-12-21 08:50:14 +0100488 :param VppInterface src_if: source interface
489 :param VppInterface dst_if: destination interface
Klement Sekeraf62ae122016-10-11 11:47:09 +0200490
491 :returns: _PacketInfo object
492
493 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200494 info = _PacketInfo()
Klement Sekeradab231a2016-12-21 08:50:14 +0100495 info.index = len(cls._packet_infos)
496 info.src = src_if.sw_if_index
497 info.dst = dst_if.sw_if_index
498 if isinstance(dst_if, VppSubInterface):
499 dst_idx = dst_if.parent.sw_if_index
500 else:
501 dst_idx = dst_if.sw_if_index
502 if dst_idx in cls._packet_count_for_dst_if_idx:
503 cls._packet_count_for_dst_if_idx[dst_idx] += 1
504 else:
505 cls._packet_count_for_dst_if_idx[dst_idx] = 1
506 cls._packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200507 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200508
Damjan Marionf56b77a2016-10-03 19:44:57 +0200509 @staticmethod
510 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200511 """
512 Convert _PacketInfo object to packet payload
513
514 :param info: _PacketInfo object
515
516 :returns: string containing serialized data from packet info
517 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200518 return "%d %d %d" % (info.index, info.src, info.dst)
519
Damjan Marionf56b77a2016-10-03 19:44:57 +0200520 @staticmethod
521 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200522 """
523 Convert packet payload to _PacketInfo object
524
525 :param payload: packet payload
526
527 :returns: _PacketInfo object containing de-serialized data from payload
528
529 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200530 numbers = payload.split()
531 info = _PacketInfo()
532 info.index = int(numbers[0])
533 info.src = int(numbers[1])
534 info.dst = int(numbers[2])
535 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200536
Damjan Marionf56b77a2016-10-03 19:44:57 +0200537 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200538 """
539 Iterate over the packet info list stored in the testcase
540 Start iteration with first element if info is None
541 Continue based on index in info if info is specified
542
543 :param info: info or None
544 :returns: next info in list or None if no more infos
545 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200546 if info is None:
547 next_index = 0
548 else:
549 next_index = info.index + 1
Klement Sekeradab231a2016-12-21 08:50:14 +0100550 if next_index == len(self._packet_infos):
Damjan Marionf56b77a2016-10-03 19:44:57 +0200551 return None
552 else:
Klement Sekeradab231a2016-12-21 08:50:14 +0100553 return self._packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200554
Klement Sekeraf62ae122016-10-11 11:47:09 +0200555 def get_next_packet_info_for_interface(self, src_index, info):
556 """
557 Search the packet info list for the next packet info with same source
558 interface index
559
560 :param src_index: source interface index to search for
561 :param info: packet info - where to start the search
562 :returns: packet info or None
563
564 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200565 while True:
566 info = self.get_next_packet_info(info)
567 if info is None:
568 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200569 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200570 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200571
Klement Sekeraf62ae122016-10-11 11:47:09 +0200572 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
573 """
574 Search the packet info list for the next packet info with same source
575 and destination interface indexes
576
577 :param src_index: source interface index to search for
578 :param dst_index: destination interface index to search for
579 :param info: packet info - where to start the search
580 :returns: packet info or None
581
582 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200583 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200584 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200585 if info is None:
586 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200587 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200588 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200589
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200590 def assert_equal(self, real_value, expected_value, name_or_class=None):
591 if name_or_class is None:
Klement Sekera239790f2017-02-16 10:53:53 +0100592 self.assertEqual(real_value, expected_value)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200593 return
594 try:
595 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
596 msg = msg % (getdoc(name_or_class).strip(),
597 real_value, str(name_or_class(real_value)),
598 expected_value, str(name_or_class(expected_value)))
599 except:
600 msg = "Invalid %s: %s does not match expected value %s" % (
601 name_or_class, real_value, expected_value)
602
603 self.assertEqual(real_value, expected_value, msg)
604
Klement Sekerab17dd962017-01-09 07:43:48 +0100605 def assert_in_range(self,
606 real_value,
607 expected_min,
608 expected_max,
609 name=None):
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200610 if name is None:
611 msg = None
612 else:
613 msg = "Invalid %s: %s out of range <%s,%s>" % (
614 name, real_value, expected_min, expected_max)
615 self.assertTrue(expected_min <= real_value <= expected_max, msg)
616
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100617 @classmethod
618 def sleep(cls, timeout, remark=None):
619 if hasattr(cls, 'logger'):
620 cls.logger.debug("Sleeping for %ss (%s)" % (timeout, remark))
Klement Sekeraa57a9702017-02-02 06:58:07 +0100621 time.sleep(timeout)
622
Damjan Marionf56b77a2016-10-03 19:44:57 +0200623
Klement Sekera87134932017-03-07 11:39:27 +0100624class TestCasePrinter(object):
625 _shared_state = {}
626
627 def __init__(self):
628 self.__dict__ = self._shared_state
629 if not hasattr(self, "_test_case_set"):
630 self._test_case_set = set()
631
632 def print_test_case_heading_if_first_time(self, case):
633 if case.__class__ not in self._test_case_set:
634 print(double_line_delim)
635 print(colorize(getdoc(case.__class__).splitlines()[0], YELLOW))
636 print(double_line_delim)
637 self._test_case_set.add(case.__class__)
638
639
Damjan Marionf56b77a2016-10-03 19:44:57 +0200640class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200641 """
642 @property result_string
643 String variable to store the test case result string.
644 @property errors
645 List variable containing 2-tuples of TestCase instances and strings
646 holding formatted tracebacks. Each tuple represents a test which
647 raised an unexpected exception.
648 @property failures
649 List variable containing 2-tuples of TestCase instances and strings
650 holding formatted tracebacks. Each tuple represents a test where
651 a failure was explicitly signalled using the TestCase.assert*()
652 methods.
653 """
654
Damjan Marionf56b77a2016-10-03 19:44:57 +0200655 def __init__(self, stream, descriptions, verbosity):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200656 """
Klement Sekerada505f62017-01-04 12:58:53 +0100657 :param stream File descriptor to store where to report test results.
658 Set to the standard error stream by default.
659 :param descriptions Boolean variable to store information if to use
660 test case descriptions.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200661 :param verbosity Integer variable to store required verbosity level.
662 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200663 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
664 self.stream = stream
665 self.descriptions = descriptions
666 self.verbosity = verbosity
667 self.result_string = None
Klement Sekera87134932017-03-07 11:39:27 +0100668 self.printer = TestCasePrinter()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200669
Damjan Marionf56b77a2016-10-03 19:44:57 +0200670 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200671 """
672 Record a test succeeded result
673
674 :param test:
675
676 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100677 if hasattr(test, 'logger'):
678 test.logger.debug("--- addSuccess() %s.%s(%s) called"
679 % (test.__class__.__name__,
680 test._testMethodName,
681 test._testMethodDoc))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200682 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +0200683 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200684
Klement Sekeraf62ae122016-10-11 11:47:09 +0200685 def addSkip(self, test, reason):
686 """
687 Record a test skipped.
688
689 :param test:
690 :param reason:
691
692 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100693 if hasattr(test, 'logger'):
694 test.logger.debug("--- addSkip() %s.%s(%s) called, reason is %s"
695 % (test.__class__.__name__,
696 test._testMethodName,
697 test._testMethodDoc,
698 reason))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200699 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +0200700 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200701
Damjan Marionf56b77a2016-10-03 19:44:57 +0200702 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200703 """
704 Record a test failed result
705
706 :param test:
707 :param err: error message
708
709 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100710 if hasattr(test, 'logger'):
711 test.logger.debug("--- addFailure() %s.%s(%s) called, err is %s"
712 % (test.__class__.__name__,
713 test._testMethodName,
714 test._testMethodDoc, err))
715 test.logger.debug("formatted exception is:\n%s" %
716 "".join(format_exception(*err)))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200717 unittest.TestResult.addFailure(self, test, err)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200718 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +0200719 self.result_string = colorize("FAIL", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +0200720 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
721 else:
Klement Sekera277b89c2016-10-28 13:20:27 +0200722 self.result_string = colorize("FAIL", RED) + ' [no temp dir]'
Damjan Marionf56b77a2016-10-03 19:44:57 +0200723
Damjan Marionf56b77a2016-10-03 19:44:57 +0200724 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200725 """
726 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +0200727
Klement Sekeraf62ae122016-10-11 11:47:09 +0200728 :param test:
729 :param err: error message
730
731 """
Klement Sekerab91017a2017-02-09 06:04:36 +0100732 if hasattr(test, 'logger'):
733 test.logger.debug("--- addError() %s.%s(%s) called, err is %s"
734 % (test.__class__.__name__,
735 test._testMethodName,
736 test._testMethodDoc, err))
737 test.logger.debug("formatted exception is:\n%s" %
738 "".join(format_exception(*err)))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200739 unittest.TestResult.addError(self, test, err)
740 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +0200741 self.result_string = colorize("ERROR", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +0200742 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
743 else:
Klement Sekera277b89c2016-10-28 13:20:27 +0200744 self.result_string = colorize("ERROR", RED) + ' [no temp dir]'
Klement Sekeraf62ae122016-10-11 11:47:09 +0200745
Damjan Marionf56b77a2016-10-03 19:44:57 +0200746 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200747 """
748 Get test description
749
750 :param test:
751 :returns: test description
752
753 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200754 # TODO: if none print warning not raise exception
755 short_description = test.shortDescription()
756 if self.descriptions and short_description:
757 return short_description
758 else:
759 return str(test)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200760
Damjan Marionf56b77a2016-10-03 19:44:57 +0200761 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200762 """
763 Start a test
764
765 :param test:
766
767 """
Klement Sekera87134932017-03-07 11:39:27 +0100768 self.printer.print_test_case_heading_if_first_time(test)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200769 unittest.TestResult.startTest(self, test)
770 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200771 self.stream.writeln(
772 "Starting " + self.getDescription(test) + " ...")
773 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200774
Damjan Marionf56b77a2016-10-03 19:44:57 +0200775 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200776 """
777 Stop a test
778
779 :param test:
780
781 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200782 unittest.TestResult.stopTest(self, test)
783 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200784 self.stream.writeln(single_line_delim)
Klement Sekera52e84f32017-01-13 07:25:25 +0100785 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +0100786 self.result_string))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200787 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200788 else:
Klement Sekera52e84f32017-01-13 07:25:25 +0100789 self.stream.writeln("%-73s%s" % (self.getDescription(test),
Klement Sekerada505f62017-01-04 12:58:53 +0100790 self.result_string))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200791
Damjan Marionf56b77a2016-10-03 19:44:57 +0200792 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200793 """
794 Print errors from running the test case
795 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200796 self.stream.writeln()
797 self.printErrorList('ERROR', self.errors)
798 self.printErrorList('FAIL', self.failures)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200799
Damjan Marionf56b77a2016-10-03 19:44:57 +0200800 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200801 """
802 Print error list to the output stream together with error type
803 and test case description.
804
805 :param flavour: error type
806 :param errors: iterable errors
807
808 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200809 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200810 self.stream.writeln(double_line_delim)
811 self.stream.writeln("%s: %s" %
812 (flavour, self.getDescription(test)))
813 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200814 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200815
816
Damjan Marionf56b77a2016-10-03 19:44:57 +0200817class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200818 """
Klement Sekera104543f2017-02-03 07:29:43 +0100819 A basic test runner implementation which prints results to standard error.
Klement Sekeraf62ae122016-10-11 11:47:09 +0200820 """
821 @property
822 def resultclass(self):
823 """Class maintaining the results of the tests"""
824 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +0200825
Klement Sekera7a161da2017-01-17 13:42:48 +0100826 def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1,
827 failfast=False, buffer=False, resultclass=None):
828 # ignore stream setting here, use hard-coded stdout to be in sync
829 # with prints from VppTestCase methods ...
830 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
831 verbosity, failfast, buffer,
832 resultclass)
833
Klement Sekera104543f2017-02-03 07:29:43 +0100834 test_option = "TEST"
835
836 def parse_test_option(self):
837 try:
838 f = os.getenv(self.test_option)
839 except:
840 f = None
841 filter_file_name = None
842 filter_class_name = None
843 filter_func_name = None
844 if f:
845 if '.' in f:
846 parts = f.split('.')
847 if len(parts) > 3:
848 raise Exception("Unrecognized %s option: %s" %
849 (self.test_option, f))
850 if len(parts) > 2:
851 if parts[2] not in ('*', ''):
852 filter_func_name = parts[2]
853 if parts[1] not in ('*', ''):
854 filter_class_name = parts[1]
855 if parts[0] not in ('*', ''):
856 if parts[0].startswith('test_'):
857 filter_file_name = parts[0]
858 else:
859 filter_file_name = 'test_%s' % parts[0]
860 else:
861 if f.startswith('test_'):
862 filter_file_name = f
863 else:
864 filter_file_name = 'test_%s' % f
865 return filter_file_name, filter_class_name, filter_func_name
866
867 def filter_tests(self, tests, filter_file, filter_class, filter_func):
868 result = unittest.suite.TestSuite()
869 for t in tests:
870 if isinstance(t, unittest.suite.TestSuite):
871 # this is a bunch of tests, recursively filter...
872 x = self.filter_tests(t, filter_file, filter_class,
873 filter_func)
874 if x.countTestCases() > 0:
875 result.addTest(x)
876 elif isinstance(t, unittest.TestCase):
877 # this is a single test
878 parts = t.id().split('.')
879 # t.id() for common cases like this:
880 # test_classifier.TestClassifier.test_acl_ip
881 # apply filtering only if it is so
882 if len(parts) == 3:
883 if filter_file and filter_file != parts[0]:
884 continue
885 if filter_class and filter_class != parts[1]:
886 continue
887 if filter_func and filter_func != parts[2]:
888 continue
889 result.addTest(t)
890 else:
891 # unexpected object, don't touch it
892 result.addTest(t)
893 return result
894
Damjan Marionf56b77a2016-10-03 19:44:57 +0200895 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200896 """
897 Run the tests
898
899 :param test:
900
901 """
Klement Sekeraacb9b8e2017-02-14 02:55:31 +0100902 gc.disable() # disable garbage collection, we'll do that manually
Klement Sekeraf62ae122016-10-11 11:47:09 +0200903 print("Running tests using custom test runner") # debug message
Klement Sekera104543f2017-02-03 07:29:43 +0100904 filter_file, filter_class, filter_func = self.parse_test_option()
905 print("Active filters: file=%s, class=%s, function=%s" % (
906 filter_file, filter_class, filter_func))
907 filtered = self.filter_tests(test, filter_file, filter_class,
908 filter_func)
909 print("%s out of %s tests match specified filters" % (
910 filtered.countTestCases(), test.countTestCases()))
911 return super(VppTestRunner, self).run(filtered)