blob: ed71908ca2dfab67df9558f49d45373461b96280 [file] [log] [blame]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001#!/usr/bin/env python
Damjan Marionf56b77a2016-10-03 19:44:57 +02002
Klement Sekeraf62ae122016-10-11 11:47:09 +02003from abc import *
Damjan Marionf56b77a2016-10-03 19:44:57 +02004import os
5import subprocess
6import unittest
Klement Sekeraf62ae122016-10-11 11:47:09 +02007import tempfile
Klement Sekera277b89c2016-10-28 13:20:27 +02008import time
Klement Sekeraf62ae122016-10-11 11:47:09 +02009import resource
10from time import sleep
Klement Sekera277b89c2016-10-28 13:20:27 +020011from Queue import Queue
12from threading import Thread
Damjan Marionf56b77a2016-10-03 19:44:57 +020013from inspect import getdoc
Klement Sekera277b89c2016-10-28 13:20:27 +020014from hook import StepHook, PollHook
Klement Sekeraf62ae122016-10-11 11:47:09 +020015from vpp_pg_interface import VppPGInterface
16from vpp_papi_provider import VppPapiProvider
Damjan Marionf56b77a2016-10-03 19:44:57 +020017from scapy.packet import Raw
Klement Sekera277b89c2016-10-28 13:20:27 +020018from log import *
Klement Sekeraf62ae122016-10-11 11:47:09 +020019
20"""
21 Test framework module.
22
23 The module provides a set of tools for constructing and running tests and
24 representing the results.
25"""
26
Klement Sekeraf62ae122016-10-11 11:47:09 +020027
Damjan Marionf56b77a2016-10-03 19:44:57 +020028class _PacketInfo(object):
Klement Sekeraf62ae122016-10-11 11:47:09 +020029 """Private class to create packet info object.
30
31 Help process information about the next packet.
32 Set variables to default values.
33 @property index
34 Integer variable to store the index of the packet.
35 @property src
36 Integer variable to store the index of the source packet generator
37 interface of the packet.
38 @property dst
39 Integer variable to store the index of the destination packet generator
40 interface of the packet.
41 @property data
42 Object variable to store the copy of the former packet.
43
44
45 """
Damjan Marionf56b77a2016-10-03 19:44:57 +020046 index = -1
47 src = -1
48 dst = -1
49 data = None
Damjan Marionf56b77a2016-10-03 19:44:57 +020050
Klement Sekeraf62ae122016-10-11 11:47:09 +020051
Klement Sekera277b89c2016-10-28 13:20:27 +020052def pump_output(out, queue):
53 for line in iter(out.readline, b''):
54 queue.put(line)
55
56
Damjan Marionf56b77a2016-10-03 19:44:57 +020057class VppTestCase(unittest.TestCase):
Klement Sekeraf62ae122016-10-11 11:47:09 +020058 """
59 Subclass of the python unittest.TestCase class.
Damjan Marionf56b77a2016-10-03 19:44:57 +020060
Klement Sekeraf62ae122016-10-11 11:47:09 +020061 This subclass is a base class for test cases that are implemented as classes
62 It provides methods to create and run test case.
63
64 """
65
66 @property
67 def packet_infos(self):
68 """List of packet infos"""
69 return self._packet_infos
70
71 @packet_infos.setter
72 def packet_infos(self, value):
73 self._packet_infos = value
74
75 @classmethod
76 def instance(cls):
77 """Return the instance of this testcase"""
78 return cls.test_instance
79
Damjan Marionf56b77a2016-10-03 19:44:57 +020080 @classmethod
Klement Sekera277b89c2016-10-28 13:20:27 +020081 def set_debug_flags(cls, d):
82 cls.debug_core = False
83 cls.debug_gdb = False
84 cls.debug_gdbserver = False
85 if d is None:
86 return
87 dl = d.lower()
88 if dl == "core":
89 if resource.getrlimit(resource.RLIMIT_CORE)[0] <= 0:
90 # give a heads up if this is actually useless
91 cls.logger.critical("WARNING: core size limit is set 0, core "
92 "files will NOT be created")
93 cls.debug_core = True
94 elif dl == "gdb":
95 cls.debug_gdb = True
96 elif dl == "gdbserver":
97 cls.debug_gdbserver = True
98 else:
99 raise Exception("Unrecognized DEBUG option: '%s'" % d)
100
101 @classmethod
Damjan Marionf56b77a2016-10-03 19:44:57 +0200102 def setUpConstants(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200103 """ Set-up the test case class based on environment variables """
104 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200105 s = os.getenv("STEP")
106 cls.step = True if s.lower() in ("y", "yes", "1") else False
Klement Sekeraf62ae122016-10-11 11:47:09 +0200107 except:
Klement Sekera277b89c2016-10-28 13:20:27 +0200108 cls.step = False
109 try:
110 d = os.getenv("DEBUG")
111 except:
112 d = None
113 cls.set_debug_flags(d)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200114 cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp")
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100115 cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
Klement Sekera01bbbe92016-11-02 09:25:05 +0100116 debug_cli = ""
117 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
118 debug_cli = "cli-listen localhost:5002"
119 cls.vpp_cmdline = [cls.vpp_bin, "unix", "{", "nodaemon", debug_cli, "}",
Klement Sekeraf62ae122016-10-11 11:47:09 +0200120 "api-segment", "{", "prefix", cls.shm_prefix, "}"]
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100121 if cls.plugin_path is not None:
122 cls.vpp_cmdline.extend(["plugin_path", cls.plugin_path])
Klement Sekera277b89c2016-10-28 13:20:27 +0200123 cls.logger.info("vpp_cmdline: %s" % cls.vpp_cmdline)
124
125 @classmethod
126 def wait_for_enter(cls):
127 if cls.debug_gdbserver:
128 print(double_line_delim)
129 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
130 elif cls.debug_gdb:
131 print(double_line_delim)
132 print("Spawned VPP with PID: %d" % cls.vpp.pid)
133 else:
134 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
135 return
136 print(single_line_delim)
137 print("You can debug the VPP using e.g.:")
138 if cls.debug_gdbserver:
139 print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
140 print("Now is the time to attach a gdb by running the above "
141 "command, set up breakpoints etc. and then resume VPP from "
142 "within gdb by issuing the 'continue' command")
143 elif cls.debug_gdb:
144 print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
145 print("Now is the time to attach a gdb by running the above "
146 "command and set up breakpoints etc.")
147 print(single_line_delim)
148 raw_input("Press ENTER to continue running the testcase...")
149
150 @classmethod
151 def run_vpp(cls):
152 cmdline = cls.vpp_cmdline
153
154 if cls.debug_gdbserver:
155 cmdline = ['gdbserver', 'localhost:7777'] + cls.vpp_cmdline
156 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
157
158 cls.vpp = subprocess.Popen(cmdline,
159 stdout=subprocess.PIPE,
160 stderr=subprocess.PIPE,
161 bufsize=1)
162 cls.wait_for_enter()
Pierre Pfistercd8e3182016-10-07 16:30:03 +0100163
Damjan Marionf56b77a2016-10-03 19:44:57 +0200164 @classmethod
165 def setUpClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200166 """
167 Perform class setup before running the testcase
168 Remove shared memory files, start vpp and connect the vpp-api
169 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200170 cls.logger = getLogger(cls.__name__)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200171 cls.tempdir = tempfile.mkdtemp(
172 prefix='vpp-unittest-' + cls.__name__ + '-')
173 cls.shm_prefix = cls.tempdir.split("/")[-1]
174 os.chdir(cls.tempdir)
Klement Sekera277b89c2016-10-28 13:20:27 +0200175 cls.logger.info("Temporary dir is %s, shm prefix is %s",
176 cls.tempdir, cls.shm_prefix)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200177 cls.setUpConstants()
178 cls.pg_streams = []
Damjan Marionf56b77a2016-10-03 19:44:57 +0200179 cls.packet_infos = {}
Klement Sekeraf62ae122016-10-11 11:47:09 +0200180 cls.verbose = 0
181 print(double_line_delim)
Klement Sekera277b89c2016-10-28 13:20:27 +0200182 print(colorize(getdoc(cls), YELLOW))
Klement Sekeraf62ae122016-10-11 11:47:09 +0200183 print(double_line_delim)
184 # need to catch exceptions here because if we raise, then the cleanup
185 # doesn't get called and we might end with a zombie vpp
186 try:
Klement Sekera277b89c2016-10-28 13:20:27 +0200187 cls.run_vpp()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200188 cls.vpp_dead = False
189 cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix)
Klement Sekera277b89c2016-10-28 13:20:27 +0200190 if cls.step:
191 cls.vapi.register_hook(StepHook(cls))
192 else:
193 cls.vapi.register_hook(PollHook(cls))
194 time.sleep(0.1)
195 try:
196 cls.vapi.connect()
197 except:
198 if cls.debug_gdbserver:
199 print(colorize("You're running VPP inside gdbserver but "
200 "VPP-API connection failed, did you forget "
201 "to 'continue' VPP from within gdb?", RED))
202 raise
203 cls.vpp_stdout_queue = Queue()
204 cls.vpp_stdout_reader_thread = Thread(
205 target=pump_output, args=(cls.vpp.stdout, cls.vpp_stdout_queue))
206 cls.vpp_stdout_reader_thread.start()
207 cls.vpp_stderr_queue = Queue()
208 cls.vpp_stderr_reader_thread = Thread(
209 target=pump_output, args=(cls.vpp.stderr, cls.vpp_stderr_queue))
210 cls.vpp_stderr_reader_thread.start()
Klement Sekeraf62ae122016-10-11 11:47:09 +0200211 except:
212 cls.vpp.terminate()
213 del cls.vpp
Klement Sekera277b89c2016-10-28 13:20:27 +0200214 raise
Damjan Marionf56b77a2016-10-03 19:44:57 +0200215
Damjan Marionf56b77a2016-10-03 19:44:57 +0200216 @classmethod
217 def quit(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200218 """
219 Disconnect vpp-api, kill vpp and cleanup shared memory files
220 """
Klement Sekera277b89c2016-10-28 13:20:27 +0200221 if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
222 cls.vpp.poll()
223 if cls.vpp.returncode is None:
224 print(double_line_delim)
225 print("VPP or GDB server is still running")
226 print(single_line_delim)
227 raw_input("When done debugging, press ENTER to kill the process"
228 " and finish running the testcase...")
229
Klement Sekeraf62ae122016-10-11 11:47:09 +0200230 if hasattr(cls, 'vpp'):
231 cls.vapi.disconnect()
232 cls.vpp.poll()
233 if cls.vpp.returncode is None:
234 cls.vpp.terminate()
235 del cls.vpp
Damjan Marionf56b77a2016-10-03 19:44:57 +0200236
Klement Sekera277b89c2016-10-28 13:20:27 +0200237 if hasattr(cls, 'vpp_stdout_queue'):
238 cls.logger.info(single_line_delim)
239 cls.logger.info('VPP output to stdout while running %s:',
240 cls.__name__)
241 cls.logger.info(single_line_delim)
242 f = open(cls.tempdir + '/vpp_stdout.txt', 'w')
243 while not cls.vpp_stdout_queue.empty():
244 line = cls.vpp_stdout_queue.get_nowait()
245 f.write(line)
246 cls.logger.info('VPP stdout: %s' % line.rstrip('\n'))
247
248 if hasattr(cls, 'vpp_stderr_queue'):
249 cls.logger.info(single_line_delim)
250 cls.logger.info('VPP output to stderr while running %s:',
251 cls.__name__)
252 cls.logger.info(single_line_delim)
253 f = open(cls.tempdir + '/vpp_stderr.txt', 'w')
254 while not cls.vpp_stderr_queue.empty():
255 line = cls.vpp_stderr_queue.get_nowait()
256 f.write(line)
257 cls.logger.info('VPP stderr: %s' % line.rstrip('\n'))
258 cls.logger.info(single_line_delim)
259
Damjan Marionf56b77a2016-10-03 19:44:57 +0200260 @classmethod
261 def tearDownClass(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200262 """ Perform final cleanup after running all tests in this test-case """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200263 cls.quit()
264
Damjan Marionf56b77a2016-10-03 19:44:57 +0200265 def tearDown(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200266 """ Show various debug prints after each test """
267 if not self.vpp_dead:
Klement Sekera277b89c2016-10-28 13:20:27 +0200268 self.logger.info(self.vapi.cli("show int"))
269 self.logger.info(self.vapi.cli("show trace"))
270 self.logger.info(self.vapi.cli("show hardware"))
271 self.logger.info(self.vapi.cli("show error"))
272 self.logger.info(self.vapi.cli("show run"))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200273
Damjan Marionf56b77a2016-10-03 19:44:57 +0200274 def setUp(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200275 """ Clear trace before running each test"""
276 self.vapi.cli("clear trace")
277 # store the test instance inside the test class - so that objects
278 # holding the class can access instance methods (like assertEqual)
279 type(self).test_instance = self
Damjan Marionf56b77a2016-10-03 19:44:57 +0200280
Damjan Marionf56b77a2016-10-03 19:44:57 +0200281 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200282 def pg_enable_capture(cls, interfaces):
283 """
284 Enable capture on packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200285
Klement Sekeraf62ae122016-10-11 11:47:09 +0200286 :param interfaces: iterable interface indexes
Damjan Marionf56b77a2016-10-03 19:44:57 +0200287
Klement Sekeraf62ae122016-10-11 11:47:09 +0200288 """
289 for i in interfaces:
290 i.enable_capture()
Damjan Marionf56b77a2016-10-03 19:44:57 +0200291
Damjan Marionf56b77a2016-10-03 19:44:57 +0200292 @classmethod
293 def pg_start(cls):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200294 """
295 Enable the packet-generator and send all prepared packet streams
296 Remove the packet streams afterwards
297 """
298 cls.vapi.cli("trace add pg-input 50") # 50 is maximum
299 cls.vapi.cli('packet-generator enable')
300 sleep(1) # give VPP some time to process the packets
Damjan Marionf56b77a2016-10-03 19:44:57 +0200301 for stream in cls.pg_streams:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200302 cls.vapi.cli('packet-generator delete %s' % stream)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200303 cls.pg_streams = []
304
Damjan Marionf56b77a2016-10-03 19:44:57 +0200305 @classmethod
Klement Sekeraf62ae122016-10-11 11:47:09 +0200306 def create_pg_interfaces(cls, interfaces):
307 """
308 Create packet-generator interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200309
Klement Sekeraf62ae122016-10-11 11:47:09 +0200310 :param interfaces: iterable indexes of the interfaces
Damjan Marionf56b77a2016-10-03 19:44:57 +0200311
Klement Sekeraf62ae122016-10-11 11:47:09 +0200312 """
313 result = []
314 for i in interfaces:
315 intf = VppPGInterface(cls, i)
316 setattr(cls, intf.name, intf)
317 result.append(intf)
318 cls.pg_interfaces = result
319 return result
320
Damjan Marionf56b77a2016-10-03 19:44:57 +0200321 @staticmethod
322 def extend_packet(packet, size):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200323 """
324 Extend packet to given size by padding with spaces
325 NOTE: Currently works only when Raw layer is present.
326
327 :param packet: packet
328 :param size: target size
329
330 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200331 packet_len = len(packet) + 4
332 extend = size - packet_len
333 if extend > 0:
334 packet[Raw].load += ' ' * extend
Damjan Marionf56b77a2016-10-03 19:44:57 +0200335
Damjan Marionf56b77a2016-10-03 19:44:57 +0200336 def add_packet_info_to_list(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200337 """
338 Add packet info to the testcase's packet info list
339
340 :param info: packet info
341
342 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200343 info.index = len(self.packet_infos)
344 self.packet_infos[info.index] = info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200345
Klement Sekeraf62ae122016-10-11 11:47:09 +0200346 def create_packet_info(self, src_pg_index, dst_pg_index):
347 """
348 Create packet info object containing the source and destination indexes
349 and add it to the testcase's packet info list
350
351 :param src_pg_index: source packet-generator index
352 :param dst_pg_index: destination packet-generator index
353
354 :returns: _PacketInfo object
355
356 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200357 info = _PacketInfo()
358 self.add_packet_info_to_list(info)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200359 info.src = src_pg_index
360 info.dst = dst_pg_index
Damjan Marionf56b77a2016-10-03 19:44:57 +0200361 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200362
Damjan Marionf56b77a2016-10-03 19:44:57 +0200363 @staticmethod
364 def info_to_payload(info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200365 """
366 Convert _PacketInfo object to packet payload
367
368 :param info: _PacketInfo object
369
370 :returns: string containing serialized data from packet info
371 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200372 return "%d %d %d" % (info.index, info.src, info.dst)
373
Damjan Marionf56b77a2016-10-03 19:44:57 +0200374 @staticmethod
375 def payload_to_info(payload):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200376 """
377 Convert packet payload to _PacketInfo object
378
379 :param payload: packet payload
380
381 :returns: _PacketInfo object containing de-serialized data from payload
382
383 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200384 numbers = payload.split()
385 info = _PacketInfo()
386 info.index = int(numbers[0])
387 info.src = int(numbers[1])
388 info.dst = int(numbers[2])
389 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200390
Damjan Marionf56b77a2016-10-03 19:44:57 +0200391 def get_next_packet_info(self, info):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200392 """
393 Iterate over the packet info list stored in the testcase
394 Start iteration with first element if info is None
395 Continue based on index in info if info is specified
396
397 :param info: info or None
398 :returns: next info in list or None if no more infos
399 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200400 if info is None:
401 next_index = 0
402 else:
403 next_index = info.index + 1
404 if next_index == len(self.packet_infos):
405 return None
406 else:
407 return self.packet_infos[next_index]
Damjan Marionf56b77a2016-10-03 19:44:57 +0200408
Klement Sekeraf62ae122016-10-11 11:47:09 +0200409 def get_next_packet_info_for_interface(self, src_index, info):
410 """
411 Search the packet info list for the next packet info with same source
412 interface index
413
414 :param src_index: source interface index to search for
415 :param info: packet info - where to start the search
416 :returns: packet info or None
417
418 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200419 while True:
420 info = self.get_next_packet_info(info)
421 if info is None:
422 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200423 if info.src == src_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200424 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200425
Klement Sekeraf62ae122016-10-11 11:47:09 +0200426 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
427 """
428 Search the packet info list for the next packet info with same source
429 and destination interface indexes
430
431 :param src_index: source interface index to search for
432 :param dst_index: destination interface index to search for
433 :param info: packet info - where to start the search
434 :returns: packet info or None
435
436 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200437 while True:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200438 info = self.get_next_packet_info_for_interface(src_index, info)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200439 if info is None:
440 return None
Klement Sekeraf62ae122016-10-11 11:47:09 +0200441 if info.dst == dst_index:
Damjan Marionf56b77a2016-10-03 19:44:57 +0200442 return info
Damjan Marionf56b77a2016-10-03 19:44:57 +0200443
444
Damjan Marionf56b77a2016-10-03 19:44:57 +0200445class VppTestResult(unittest.TestResult):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200446 """
447 @property result_string
448 String variable to store the test case result string.
449 @property errors
450 List variable containing 2-tuples of TestCase instances and strings
451 holding formatted tracebacks. Each tuple represents a test which
452 raised an unexpected exception.
453 @property failures
454 List variable containing 2-tuples of TestCase instances and strings
455 holding formatted tracebacks. Each tuple represents a test where
456 a failure was explicitly signalled using the TestCase.assert*()
457 methods.
458 """
459
Damjan Marionf56b77a2016-10-03 19:44:57 +0200460 def __init__(self, stream, descriptions, verbosity):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200461 """
462 :param stream File descriptor to store where to report test results. Set
463 to the standard error stream by default.
464 :param descriptions Boolean variable to store information if to use test
465 case descriptions.
466 :param verbosity Integer variable to store required verbosity level.
467 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200468 unittest.TestResult.__init__(self, stream, descriptions, verbosity)
469 self.stream = stream
470 self.descriptions = descriptions
471 self.verbosity = verbosity
472 self.result_string = None
Damjan Marionf56b77a2016-10-03 19:44:57 +0200473
Damjan Marionf56b77a2016-10-03 19:44:57 +0200474 def addSuccess(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200475 """
476 Record a test succeeded result
477
478 :param test:
479
480 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200481 unittest.TestResult.addSuccess(self, test)
Klement Sekera277b89c2016-10-28 13:20:27 +0200482 self.result_string = colorize("OK", GREEN)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200483
Klement Sekeraf62ae122016-10-11 11:47:09 +0200484 def addSkip(self, test, reason):
485 """
486 Record a test skipped.
487
488 :param test:
489 :param reason:
490
491 """
492 unittest.TestResult.addSkip(self, test, reason)
Klement Sekera277b89c2016-10-28 13:20:27 +0200493 self.result_string = colorize("SKIP", YELLOW)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200494
Damjan Marionf56b77a2016-10-03 19:44:57 +0200495 def addFailure(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200496 """
497 Record a test failed result
498
499 :param test:
500 :param err: error message
501
502 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200503 unittest.TestResult.addFailure(self, test, err)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200504 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +0200505 self.result_string = colorize("FAIL", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +0200506 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
507 else:
Klement Sekera277b89c2016-10-28 13:20:27 +0200508 self.result_string = colorize("FAIL", RED) + ' [no temp dir]'
Damjan Marionf56b77a2016-10-03 19:44:57 +0200509
Damjan Marionf56b77a2016-10-03 19:44:57 +0200510 def addError(self, test, err):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200511 """
512 Record a test error result
Damjan Marionf56b77a2016-10-03 19:44:57 +0200513
Klement Sekeraf62ae122016-10-11 11:47:09 +0200514 :param test:
515 :param err: error message
516
517 """
518 unittest.TestResult.addError(self, test, err)
519 if hasattr(test, 'tempdir'):
Klement Sekera277b89c2016-10-28 13:20:27 +0200520 self.result_string = colorize("ERROR", RED) + \
Klement Sekeraf62ae122016-10-11 11:47:09 +0200521 ' [ temp dir used by test case: ' + test.tempdir + ' ]'
522 else:
Klement Sekera277b89c2016-10-28 13:20:27 +0200523 self.result_string = colorize("ERROR", RED) + ' [no temp dir]'
Klement Sekeraf62ae122016-10-11 11:47:09 +0200524
Damjan Marionf56b77a2016-10-03 19:44:57 +0200525 def getDescription(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200526 """
527 Get test description
528
529 :param test:
530 :returns: test description
531
532 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200533 # TODO: if none print warning not raise exception
534 short_description = test.shortDescription()
535 if self.descriptions and short_description:
536 return short_description
537 else:
538 return str(test)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200539
Damjan Marionf56b77a2016-10-03 19:44:57 +0200540 def startTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200541 """
542 Start a test
543
544 :param test:
545
546 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200547 unittest.TestResult.startTest(self, test)
548 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200549 self.stream.writeln(
550 "Starting " + self.getDescription(test) + " ...")
551 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200552
Damjan Marionf56b77a2016-10-03 19:44:57 +0200553 def stopTest(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200554 """
555 Stop a test
556
557 :param test:
558
559 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200560 unittest.TestResult.stopTest(self, test)
561 if self.verbosity > 0:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200562 self.stream.writeln(single_line_delim)
563 self.stream.writeln("%-60s%s" %
564 (self.getDescription(test), self.result_string))
565 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200566 else:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200567 self.stream.writeln("%-60s%s" %
568 (self.getDescription(test), self.result_string))
Damjan Marionf56b77a2016-10-03 19:44:57 +0200569
Damjan Marionf56b77a2016-10-03 19:44:57 +0200570 def printErrors(self):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200571 """
572 Print errors from running the test case
573 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200574 self.stream.writeln()
575 self.printErrorList('ERROR', self.errors)
576 self.printErrorList('FAIL', self.failures)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200577
Damjan Marionf56b77a2016-10-03 19:44:57 +0200578 def printErrorList(self, flavour, errors):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200579 """
580 Print error list to the output stream together with error type
581 and test case description.
582
583 :param flavour: error type
584 :param errors: iterable errors
585
586 """
Damjan Marionf56b77a2016-10-03 19:44:57 +0200587 for test, err in errors:
Klement Sekeraf62ae122016-10-11 11:47:09 +0200588 self.stream.writeln(double_line_delim)
589 self.stream.writeln("%s: %s" %
590 (flavour, self.getDescription(test)))
591 self.stream.writeln(single_line_delim)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200592 self.stream.writeln("%s" % err)
Damjan Marionf56b77a2016-10-03 19:44:57 +0200593
594
Damjan Marionf56b77a2016-10-03 19:44:57 +0200595class VppTestRunner(unittest.TextTestRunner):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200596 """
597 A basic test runner implementation which prints results on standard error.
598 """
599 @property
600 def resultclass(self):
601 """Class maintaining the results of the tests"""
602 return VppTestResult
Damjan Marionf56b77a2016-10-03 19:44:57 +0200603
Damjan Marionf56b77a2016-10-03 19:44:57 +0200604 def run(self, test):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200605 """
606 Run the tests
607
608 :param test:
609
610 """
611 print("Running tests using custom test runner") # debug message
Damjan Marionf56b77a2016-10-03 19:44:57 +0200612 return super(VppTestRunner, self).run(test)