blob: 89c7797d5ba8bfca9b4307fdaae9f37eb5f5f1fd [file] [log] [blame]
Damjan Marionf56b77a2016-10-03 19:44:57 +02001#!/usr/bin/env python
2
Klement Sekera993e0ed2017-03-16 09:14:59 +01003import sys
Dave Wallacee2efd122017-09-30 22:04:21 -04004import shutil
Damjan Marionf56b77a2016-10-03 19:44:57 +02005import os
Klement Sekera909a6a12017-08-08 04:33:53 +02006import select
Damjan Marionf56b77a2016-10-03 19:44:57 +02007import unittest
Klement Sekera993e0ed2017-03-16 09:14:59 +01008import argparse
Klement Sekera545be522018-02-16 19:25:06 +01009import time
juraj.linkes184870a2018-07-16 14:22:01 +020010import threading
11import signal
12import psutil
13from multiprocessing import Process, Pipe, cpu_count
14from multiprocessing.queues import Queue
15from multiprocessing.managers import BaseManager
16from framework import VppTestRunner, running_extended_tests, VppTestCase, \
17 get_testcase_doc_name, get_test_description
Klement Sekera909a6a12017-08-08 04:33:53 +020018from debug import spawn_gdb
juraj.linkes184870a2018-07-16 14:22:01 +020019from log import get_parallel_logger, double_line_delim, RED, YELLOW, GREEN, \
20 colorize
Klement Sekerafcbf4442017-08-17 07:38:42 +020021from discover_tests import discover_tests
Klement Sekera9b6ece72018-03-23 10:50:11 +010022from subprocess import check_output, CalledProcessError
Andrew Yourtchenko57612eb2018-03-28 15:32:10 +020023from util import check_core_path
Klement Sekera993e0ed2017-03-16 09:14:59 +010024
Klement Sekera05742262018-03-14 18:14:49 +010025# timeout which controls how long the child has to finish after seeing
26# a core dump in test temporary directory. If this is exceeded, parent assumes
27# that child process is stuck (e.g. waiting for shm mutex, which will never
28# get unlocked) and kill the child
29core_timeout = 3
juraj.linkes184870a2018-07-16 14:22:01 +020030min_req_shm = 536870912 # min 512MB shm required
31# 128MB per extra process
32shm_per_process = 134217728
Klement Sekera05742262018-03-14 18:14:49 +010033
Klement Sekera909a6a12017-08-08 04:33:53 +020034
juraj.linkes184870a2018-07-16 14:22:01 +020035class StreamQueue(Queue):
36 def write(self, msg):
37 self.put(msg)
38
39 def flush(self):
40 sys.__stdout__.flush()
41 sys.__stderr__.flush()
42
43 def fileno(self):
44 return self._writer.fileno()
45
46
47class StreamQueueManager(BaseManager):
48 pass
49
50
51StreamQueueManager.register('Queue', StreamQueue)
52
53
54def test_runner_wrapper(suite, keep_alive_pipe, result_pipe, stdouterr_queue,
juraj.linkes5e2c54d2018-08-30 10:51:45 +020055 partial_result_queue, logger):
juraj.linkes184870a2018-07-16 14:22:01 +020056 sys.stdout = stdouterr_queue
57 sys.stderr = stdouterr_queue
58 VppTestCase.logger = logger
59 result = VppTestRunner(keep_alive_pipe=keep_alive_pipe,
60 descriptions=descriptions,
61 verbosity=verbose,
juraj.linkes5e2c54d2018-08-30 10:51:45 +020062 results_pipe=partial_result_queue,
juraj.linkes184870a2018-07-16 14:22:01 +020063 failfast=failfast).run(suite)
Klement Sekera909a6a12017-08-08 04:33:53 +020064 result_pipe.send(result)
65 result_pipe.close()
66 keep_alive_pipe.close()
67
68
juraj.linkes184870a2018-07-16 14:22:01 +020069class TestCaseWrapper(object):
70 def __init__(self, testcase_suite, manager):
71 self.keep_alive_parent_end, self.keep_alive_child_end = Pipe(
72 duplex=False)
73 self.result_parent_end, self.result_child_end = Pipe(duplex=False)
juraj.linkes5e2c54d2018-08-30 10:51:45 +020074 self.partial_result_parent_end, self.partial_result_child_end = Pipe(
75 duplex=False)
juraj.linkes184870a2018-07-16 14:22:01 +020076 self.testcase_suite = testcase_suite
77 self.stdouterr_queue = manager.Queue()
78 self.logger = get_parallel_logger(self.stdouterr_queue)
79 self.child = Process(target=test_runner_wrapper,
80 args=(testcase_suite, self.keep_alive_child_end,
81 self.result_child_end, self.stdouterr_queue,
juraj.linkes5e2c54d2018-08-30 10:51:45 +020082 self.partial_result_child_end, self.logger)
juraj.linkes184870a2018-07-16 14:22:01 +020083 )
84 self.child.start()
85 self.pid = self.child.pid
86 self.last_test_temp_dir = None
87 self.last_test_vpp_binary = None
88 self.last_test = None
89 self.result = None
juraj.linkes721872e2018-09-05 18:13:45 +020090 self.vpp_pid = None
juraj.linkes184870a2018-07-16 14:22:01 +020091 self.last_heard = time.time()
92 self.core_detected_at = None
93 self.failed_tests = []
juraj.linkes5e2c54d2018-08-30 10:51:45 +020094 self.partial_result = None
juraj.linkes184870a2018-07-16 14:22:01 +020095
96 def close_pipes(self):
97 self.keep_alive_child_end.close()
98 self.result_child_end.close()
juraj.linkes5e2c54d2018-08-30 10:51:45 +020099 self.partial_result_child_end.close()
juraj.linkes184870a2018-07-16 14:22:01 +0200100 self.keep_alive_parent_end.close()
101 self.result_parent_end.close()
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200102 self.partial_result_parent_end.close()
juraj.linkes184870a2018-07-16 14:22:01 +0200103
104
105def stdouterr_reader_wrapper(unread_testcases, finished_unread_testcases,
106 read_testcases):
107 read_testcase = None
108 while read_testcases.is_set() or len(unread_testcases) > 0:
109 if not read_testcase:
110 if len(finished_unread_testcases) > 0:
111 read_testcase = finished_unread_testcases.pop()
112 unread_testcases.remove(read_testcase)
113 elif len(unread_testcases) > 0:
114 read_testcase = unread_testcases.pop()
115 if read_testcase:
116 data = ''
117 while data is not None:
118 sys.stdout.write(data)
119 data = read_testcase.stdouterr_queue.get()
120
121 read_testcase.stdouterr_queue.close()
122 finished_unread_testcases.discard(read_testcase)
123 read_testcase = None
124
125
juraj.linkes721872e2018-09-05 18:13:45 +0200126def run_forked(testcase_suites):
juraj.linkes184870a2018-07-16 14:22:01 +0200127 wrapped_testcase_suites = set()
128
129 # suites are unhashable, need to use list
130 results = []
131 debug_core = os.getenv("DEBUG", "").lower() == "core"
132 unread_testcases = set()
133 finished_unread_testcases = set()
134 manager = StreamQueueManager()
135 manager.start()
136 for i in range(concurrent_tests):
juraj.linkes721872e2018-09-05 18:13:45 +0200137 if len(testcase_suites) > 0:
138 wrapped_testcase_suite = TestCaseWrapper(testcase_suites.pop(0),
139 manager)
juraj.linkes184870a2018-07-16 14:22:01 +0200140 wrapped_testcase_suites.add(wrapped_testcase_suite)
141 unread_testcases.add(wrapped_testcase_suite)
142 # time.sleep(1)
143 else:
144 break
145
146 read_from_testcases = threading.Event()
147 read_from_testcases.set()
148 stdouterr_thread = threading.Thread(target=stdouterr_reader_wrapper,
149 args=(unread_testcases,
150 finished_unread_testcases,
151 read_from_testcases))
152 stdouterr_thread.start()
153
154 while len(wrapped_testcase_suites) > 0:
155 finished_testcase_suites = set()
156 for wrapped_testcase_suite in wrapped_testcase_suites:
157 readable = select.select(
158 [wrapped_testcase_suite.keep_alive_parent_end.fileno(),
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200159 wrapped_testcase_suite.result_parent_end.fileno(),
160 wrapped_testcase_suite.partial_result_parent_end.fileno()],
juraj.linkes184870a2018-07-16 14:22:01 +0200161 [], [], 1)[0]
162 if wrapped_testcase_suite.result_parent_end.fileno() in readable:
163 results.append(
164 (wrapped_testcase_suite.testcase_suite,
165 wrapped_testcase_suite.result_parent_end.recv()))
166 finished_testcase_suites.add(wrapped_testcase_suite)
167 continue
168
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200169 if wrapped_testcase_suite.partial_result_parent_end.fileno() \
170 in readable:
171 while wrapped_testcase_suite.partial_result_parent_end.poll():
172 wrapped_testcase_suite.partial_result = \
173 wrapped_testcase_suite.partial_result_parent_end.recv()
174 wrapped_testcase_suite.last_heard = time.time()
175
juraj.linkes184870a2018-07-16 14:22:01 +0200176 if wrapped_testcase_suite.keep_alive_parent_end.fileno() \
177 in readable:
178 while wrapped_testcase_suite.keep_alive_parent_end.poll():
179 wrapped_testcase_suite.last_test, \
180 wrapped_testcase_suite.last_test_vpp_binary, \
181 wrapped_testcase_suite.last_test_temp_dir, \
182 wrapped_testcase_suite.vpp_pid = \
183 wrapped_testcase_suite.keep_alive_parent_end.recv()
184 wrapped_testcase_suite.last_heard = time.time()
185
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200186 fail = False
187 if wrapped_testcase_suite.last_heard + test_timeout < time.time() \
188 and not os.path.isfile(
189 "%s/_core_handled" %
190 wrapped_testcase_suite.last_test_temp_dir):
191 fail = True
192 wrapped_testcase_suite.logger.critical(
193 "Timeout while waiting for child test "
194 "runner process (last test running was "
195 "`%s' in `%s')!" %
196 (wrapped_testcase_suite.last_test,
197 wrapped_testcase_suite.last_test_temp_dir))
198 elif not wrapped_testcase_suite.child.is_alive():
199 fail = True
200 wrapped_testcase_suite.logger.critical(
201 "Child python process unexpectedly died "
202 "(last test running was `%s' in `%s')!" %
203 (wrapped_testcase_suite.last_test,
204 wrapped_testcase_suite.last_test_temp_dir))
205 elif wrapped_testcase_suite.last_test_temp_dir and \
206 wrapped_testcase_suite.last_test_vpp_binary:
207 core_path = "%s/core" % \
208 wrapped_testcase_suite.last_test_temp_dir
209 if os.path.isfile(core_path):
210 if wrapped_testcase_suite.core_detected_at is None:
211 wrapped_testcase_suite.core_detected_at = time.time()
212 elif wrapped_testcase_suite.core_detected_at + \
213 core_timeout < time.time():
214 if not os.path.isfile(
215 "%s/_core_handled" %
216 wrapped_testcase_suite.
217 last_test_temp_dir):
218 wrapped_testcase_suite.logger.critical(
219 "Child python process unresponsive and core-"
220 "file exists in test temporary directory!")
221 fail = True
juraj.linkes184870a2018-07-16 14:22:01 +0200222
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200223 if fail:
juraj.linkes184870a2018-07-16 14:22:01 +0200224 failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
juraj.linkes721872e2018-09-05 18:13:45 +0200225 if wrapped_testcase_suite.last_test_temp_dir:
226 lttd = os.path.basename(
227 wrapped_testcase_suite.last_test_temp_dir)
228 else:
229 lttd = None
juraj.linkes184870a2018-07-16 14:22:01 +0200230 link_path = '%s%s-FAILED' % (failed_dir, lttd)
231 wrapped_testcase_suite.logger.error(
232 "Creating a link to the failed test: %s -> %s" %
233 (link_path, lttd))
juraj.linkes721872e2018-09-05 18:13:45 +0200234 if not os.path.exists(link_path) \
235 and wrapped_testcase_suite.last_test_temp_dir:
juraj.linkes184870a2018-07-16 14:22:01 +0200236 os.symlink(wrapped_testcase_suite.last_test_temp_dir,
237 link_path)
238 api_post_mortem_path = "/tmp/api_post_mortem.%d" % \
239 wrapped_testcase_suite.vpp_pid
240 if os.path.isfile(api_post_mortem_path):
241 wrapped_testcase_suite.logger.error(
242 "Copying api_post_mortem.%d to %s" %
243 (wrapped_testcase_suite.vpp_pid,
244 wrapped_testcase_suite.last_test_temp_dir))
245 shutil.copy2(api_post_mortem_path,
246 wrapped_testcase_suite.last_test_temp_dir)
247 if wrapped_testcase_suite.last_test_temp_dir and \
248 wrapped_testcase_suite.last_test_vpp_binary:
249 core_path = "%s/core" % \
250 wrapped_testcase_suite.last_test_temp_dir
251 if os.path.isfile(core_path):
252 wrapped_testcase_suite.logger.error(
253 "Core-file exists in test temporary directory: %s!"
254 % core_path)
255 check_core_path(wrapped_testcase_suite.logger,
256 core_path)
257 wrapped_testcase_suite.logger.debug(
258 "Running `file %s':" % core_path)
259 try:
260 info = check_output(["file", core_path])
261 wrapped_testcase_suite.logger.debug(info)
262 except CalledProcessError as e:
263 wrapped_testcase_suite.logger.error(
264 "Could not run `file' utility on core-file, "
265 "rc=%s" % e.returncode)
266 pass
267 if debug_core:
268 spawn_gdb(
269 wrapped_testcase_suite.last_test_vpp_binary,
270 core_path, wrapped_testcase_suite.logger)
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200271 wrapped_testcase_suite.child.terminate()
juraj.linkes184870a2018-07-16 14:22:01 +0200272 try:
273 # terminating the child process tends to leave orphan
274 # VPP process around
275 os.kill(wrapped_testcase_suite.vpp_pid, signal.SIGTERM)
276 except OSError:
277 # already dead
278 pass
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200279 results.append((wrapped_testcase_suite.testcase_suite,
280 wrapped_testcase_suite.partial_result))
281 finished_testcase_suites.add(wrapped_testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200282
283 for finished_testcase in finished_testcase_suites:
284 finished_testcase.child.join()
285 finished_testcase.close_pipes()
286 wrapped_testcase_suites.remove(finished_testcase)
287 finished_unread_testcases.add(finished_testcase)
288 finished_testcase.stdouterr_queue.put(None)
juraj.linkes721872e2018-09-05 18:13:45 +0200289 if len(testcase_suites) > 0:
290 new_testcase = TestCaseWrapper(testcase_suites.pop(0), manager)
juraj.linkes184870a2018-07-16 14:22:01 +0200291 wrapped_testcase_suites.add(new_testcase)
292 unread_testcases.add(new_testcase)
293
294 read_from_testcases.clear()
295 stdouterr_thread.join(test_timeout)
296 manager.shutdown()
297 return results
298
299
300class SplitToSuitesCallback:
301 def __init__(self, filter_callback):
302 self.suites = {}
303 self.suite_name = 'default'
304 self.filter_callback = filter_callback
305 self.filtered = unittest.TestSuite()
Klement Sekerafcbf4442017-08-17 07:38:42 +0200306
307 def __call__(self, file_name, cls, method):
juraj.linkes184870a2018-07-16 14:22:01 +0200308 test_method = cls(method)
309 if self.filter_callback(file_name, cls.__name__, method):
310 self.suite_name = file_name + cls.__name__
311 if self.suite_name not in self.suites:
312 self.suites[self.suite_name] = unittest.TestSuite()
313 self.suites[self.suite_name].addTest(test_method)
314
315 else:
316 self.filtered.addTest(test_method)
Klement Sekerafcbf4442017-08-17 07:38:42 +0200317
318
juraj.linkes184870a2018-07-16 14:22:01 +0200319test_option = "TEST"
320
321
322def parse_test_option():
323 f = os.getenv(test_option, None)
324 filter_file_name = None
325 filter_class_name = None
326 filter_func_name = None
327 if f:
328 if '.' in f:
329 parts = f.split('.')
330 if len(parts) > 3:
331 raise Exception("Unrecognized %s option: %s" %
332 (test_option, f))
333 if len(parts) > 2:
334 if parts[2] not in ('*', ''):
335 filter_func_name = parts[2]
336 if parts[1] not in ('*', ''):
337 filter_class_name = parts[1]
338 if parts[0] not in ('*', ''):
339 if parts[0].startswith('test_'):
340 filter_file_name = parts[0]
341 else:
342 filter_file_name = 'test_%s' % parts[0]
343 else:
344 if f.startswith('test_'):
345 filter_file_name = f
346 else:
347 filter_file_name = 'test_%s' % f
348 if filter_file_name:
349 filter_file_name = '%s.py' % filter_file_name
350 return filter_file_name, filter_class_name, filter_func_name
351
352
353def filter_tests(tests, filter_cb):
354 result = unittest.suite.TestSuite()
355 for t in tests:
356 if isinstance(t, unittest.suite.TestSuite):
357 # this is a bunch of tests, recursively filter...
358 x = filter_tests(t, filter_cb)
359 if x.countTestCases() > 0:
360 result.addTest(x)
361 elif isinstance(t, unittest.TestCase):
362 # this is a single test
363 parts = t.id().split('.')
364 # t.id() for common cases like this:
365 # test_classifier.TestClassifier.test_acl_ip
366 # apply filtering only if it is so
367 if len(parts) == 3:
368 if not filter_cb(parts[0], parts[1], parts[2]):
369 continue
370 result.addTest(t)
371 else:
372 # unexpected object, don't touch it
373 result.addTest(t)
374 return result
375
376
377class FilterByTestOption:
378 def __init__(self, filter_file_name, filter_class_name, filter_func_name):
379 self.filter_file_name = filter_file_name
380 self.filter_class_name = filter_class_name
381 self.filter_func_name = filter_func_name
382
383 def __call__(self, file_name, class_name, func_name):
384 if self.filter_file_name and file_name != self.filter_file_name:
385 return False
386 if self.filter_class_name and class_name != self.filter_class_name:
387 return False
388 if self.filter_func_name and func_name != self.filter_func_name:
389 return False
390 return True
391
392
393class FilterByClassList:
juraj.linkes721872e2018-09-05 18:13:45 +0200394 def __init__(self, classes_with_filenames):
395 self.classes_with_filenames = classes_with_filenames
Klement Sekeradf2b9802017-10-05 10:26:03 +0200396
397 def __call__(self, file_name, class_name, func_name):
juraj.linkes721872e2018-09-05 18:13:45 +0200398 return '.'.join([file_name, class_name]) in self.classes_with_filenames
Klement Sekeradf2b9802017-10-05 10:26:03 +0200399
400
401def suite_from_failed(suite, failed):
juraj.linkes721872e2018-09-05 18:13:45 +0200402 failed = {x.rsplit('.', 1)[0] for x in failed}
juraj.linkes184870a2018-07-16 14:22:01 +0200403 filter_cb = FilterByClassList(failed)
404 suite = filter_tests(suite, filter_cb)
Klement Sekera4c5422e2018-06-22 13:19:45 +0200405 return suite
Klement Sekeradf2b9802017-10-05 10:26:03 +0200406
407
juraj.linkes184870a2018-07-16 14:22:01 +0200408class NonPassedResults(dict):
409 def __init__(self):
410 super(NonPassedResults, self).__init__()
411 self.all_testcases = 0
412 self.results_per_suite = {}
413 self.failures_id = 'failures'
414 self.errors_id = 'errors'
415 self.crashes_id = 'crashes'
416 self.skipped_id = 'skipped'
417 self.expectedFailures_id = 'expectedFailures'
418 self.unexpectedSuccesses_id = 'unexpectedSuccesses'
419 self.rerun = []
juraj.linkes0219b8d2018-08-24 16:16:28 +0200420 self.passed = 0
juraj.linkes184870a2018-07-16 14:22:01 +0200421 self[self.failures_id] = 0
422 self[self.errors_id] = 0
juraj.linkes184870a2018-07-16 14:22:01 +0200423 self[self.skipped_id] = 0
424 self[self.expectedFailures_id] = 0
425 self[self.unexpectedSuccesses_id] = 0
Klement Sekera909a6a12017-08-08 04:33:53 +0200426
juraj.linkes184870a2018-07-16 14:22:01 +0200427 def _add_result(self, test, result_id):
428 if isinstance(test, VppTestCase):
429 parts = test.id().split('.')
430 if len(parts) == 3:
431 tc_class = get_testcase_doc_name(test)
432 if tc_class not in self.results_per_suite:
433 # failed, errored, skipped, expectedly failed,
434 # unexpectedly passed
435 self.results_per_suite[tc_class] = \
436 {self.failures_id: [],
437 self.errors_id: [],
juraj.linkes184870a2018-07-16 14:22:01 +0200438 self.skipped_id: [],
439 self.expectedFailures_id: [],
440 self.unexpectedSuccesses_id: []}
441 self.results_per_suite[tc_class][result_id].append(test)
442 return True
443 return False
Klement Sekera05742262018-03-14 18:14:49 +0100444
juraj.linkes0219b8d2018-08-24 16:16:28 +0200445 def add_results(self, testcases, testcase_result_id):
juraj.linkes184870a2018-07-16 14:22:01 +0200446 for failed_testcase, _ in testcases:
juraj.linkes0219b8d2018-08-24 16:16:28 +0200447 if self._add_result(failed_testcase, testcase_result_id):
448 self[testcase_result_id] += 1
juraj.linkes184870a2018-07-16 14:22:01 +0200449
450 def add_result(self, testcase_suite, result):
451 retval = 0
juraj.linkes184870a2018-07-16 14:22:01 +0200452 if result:
juraj.linkes721872e2018-09-05 18:13:45 +0200453 self.all_testcases += result.testsRun
454 self.passed += len(result.passed)
455 if not len(result.passed) + len(result.skipped) \
456 == testcase_suite.countTestCases():
juraj.linkes184870a2018-07-16 14:22:01 +0200457 retval = 1
458
459 self.add_results(result.failures, self.failures_id)
juraj.linkes0219b8d2018-08-24 16:16:28 +0200460 self.add_results(result.errors, self.errors_id)
juraj.linkes184870a2018-07-16 14:22:01 +0200461 self.add_results(result.skipped, self.skipped_id)
462 self.add_results(result.expectedFailures,
463 self.expectedFailures_id)
464 self.add_results(result.unexpectedSuccesses,
465 self.unexpectedSuccesses_id)
juraj.linkes721872e2018-09-05 18:13:45 +0200466 else:
467 retval = -1
juraj.linkes184870a2018-07-16 14:22:01 +0200468
juraj.linkes184870a2018-07-16 14:22:01 +0200469 if retval != 0:
470 if concurrent_tests == 1:
471 if result:
juraj.linkes721872e2018-09-05 18:13:45 +0200472 rerun_ids = set([])
473 skipped = [x.id() for (x, _) in result.skipped]
474 for testcase in testcase_suite:
475 tc_id = testcase.id()
476 if tc_id not in result.passed and \
477 tc_id not in skipped:
478 rerun_ids.add(tc_id)
479 if len(rerun_ids) > 0:
480 self.rerun.append(suite_from_failed(testcase_suite,
481 rerun_ids))
juraj.linkes184870a2018-07-16 14:22:01 +0200482 else:
483 self.rerun.append(testcase_suite)
484 else:
485 self.rerun.append(testcase_suite)
486
487 return retval
488
489 def print_results(self):
490 print('')
491 print(double_line_delim)
492 print('TEST RESULTS:')
493 print(' Executed tests: {}'.format(self.all_testcases))
494 print(' Passed tests: {}'.format(
juraj.linkes0219b8d2018-08-24 16:16:28 +0200495 colorize(str(self.passed), GREEN)))
juraj.linkes184870a2018-07-16 14:22:01 +0200496 if self[self.failures_id] > 0:
juraj.linkes0219b8d2018-08-24 16:16:28 +0200497 print(' Failures: {}'.format(
juraj.linkes184870a2018-07-16 14:22:01 +0200498 colorize(str(self[self.failures_id]), RED)))
499 if self[self.errors_id] > 0:
juraj.linkes0219b8d2018-08-24 16:16:28 +0200500 print(' Errors: {}'.format(
juraj.linkes184870a2018-07-16 14:22:01 +0200501 colorize(str(self[self.errors_id]), RED)))
juraj.linkes184870a2018-07-16 14:22:01 +0200502 if self[self.skipped_id] > 0:
503 print(' Skipped tests: {}'.format(
504 colorize(str(self[self.skipped_id]), YELLOW)))
505 if self[self.expectedFailures_id] > 0:
506 print(' Expected failures: {}'.format(
507 colorize(str(self[self.expectedFailures_id]), GREEN)))
508 if self[self.unexpectedSuccesses_id] > 0:
509 print(' Unexpected successes: {}'.format(
510 colorize(str(self[self.unexpectedSuccesses_id]), YELLOW)))
511
512 if self.all_failed > 0:
513 print('FAILED TESTS:')
514 for testcase_class, suite_results in \
515 self.results_per_suite.items():
516 failed_testcases = suite_results[
517 self.failures_id]
518 errored_testcases = suite_results[
519 self.errors_id]
juraj.linkes0219b8d2018-08-24 16:16:28 +0200520 if len(failed_testcases) or len(errored_testcases):
juraj.linkes184870a2018-07-16 14:22:01 +0200521 print(' Testcase name: {}'.format(
522 colorize(testcase_class, RED)))
523 for failed_test in failed_testcases:
524 print(' FAILED: {}'.format(
525 colorize(get_test_description(
526 descriptions, failed_test), RED)))
527 for failed_test in errored_testcases:
528 print(' ERRORED: {}'.format(
529 colorize(get_test_description(
530 descriptions, failed_test), RED)))
juraj.linkes184870a2018-07-16 14:22:01 +0200531
532 print(double_line_delim)
533 print('')
534
535 @property
juraj.linkes184870a2018-07-16 14:22:01 +0200536 def all_failed(self):
juraj.linkes0219b8d2018-08-24 16:16:28 +0200537 return self[self.failures_id] + self[self.errors_id]
juraj.linkes184870a2018-07-16 14:22:01 +0200538
539
540def parse_results(results):
541 """
542 Prints the number of executed, passed, failed, errored, skipped,
543 expectedly failed and unexpectedly passed tests and details about
544 failed, errored, expectedly failed and unexpectedly passed tests.
545
546 Also returns any suites where any test failed.
547
548 :param results:
549 :return:
550 """
551
552 results_per_suite = NonPassedResults()
553 crashed = False
554 failed = False
555 for testcase_suite, result in results:
556 result_code = results_per_suite.add_result(testcase_suite, result)
557 if result_code == 1:
558 failed = True
559 elif result_code == -1:
560 crashed = True
561
562 results_per_suite.print_results()
563
564 if crashed:
565 return_code = -1
566 elif failed:
567 return_code = 1
568 else:
569 return_code = 0
570 return return_code, results_per_suite.rerun
571
572
573def parse_digit_env(env_var, default):
574 value = os.getenv(env_var, default)
575 if value != default:
576 if value.isdigit():
577 value = int(value)
578 else:
579 print('WARNING: unsupported value "%s" for env var "%s",'
580 'defaulting to %s' % (value, env_var, default))
581 value = default
582 return value
Klement Sekera3f6ff192017-08-11 06:56:05 +0200583
584
585if __name__ == '__main__':
586
juraj.linkes184870a2018-07-16 14:22:01 +0200587 verbose = parse_digit_env("V", 0)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200588
juraj.linkes184870a2018-07-16 14:22:01 +0200589 test_timeout = parse_digit_env("TIMEOUT", 600) # default = 10 minutes
Klement Sekera3f6ff192017-08-11 06:56:05 +0200590
juraj.linkes184870a2018-07-16 14:22:01 +0200591 retries = parse_digit_env("RETRIES", 0)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200592
juraj.linkes184870a2018-07-16 14:22:01 +0200593 debug = os.getenv("DEBUG", "n").lower() in ["gdb", "gdbserver"]
594
595 step = os.getenv("STEP", "n").lower() in ("y", "yes", "1")
596
597 force_foreground = \
598 os.getenv("FORCE_FOREGROUND", "n").lower() in ("y", "yes", "1")
599
600 run_interactive = debug or step or force_foreground
601
602 test_jobs = os.getenv("TEST_JOBS", "1").lower() # default = 1 process
603 if test_jobs == 'auto':
604 if run_interactive:
605 concurrent_tests = 1
606 print('Interactive mode required, running on one core')
607 else:
608 shm_free = psutil.disk_usage('/dev/shm').free
609 shm_max_processes = 1
610 if shm_free < min_req_shm:
611 raise Exception('Not enough free space in /dev/shm. Required '
612 'free space is at least %sM.'
613 % (min_req_shm >> 20))
614 else:
615 extra_shm = shm_free - min_req_shm
616 shm_max_processes += extra_shm / shm_per_process
617 concurrent_tests = max(cpu_count(), shm_max_processes)
618 print('Found enough resources to run tests with %s cores'
619 % concurrent_tests)
620 elif test_jobs.isdigit():
621 concurrent_tests = int(test_jobs)
622 else:
623 concurrent_tests = 1
624
625 if run_interactive and concurrent_tests > 1:
626 raise NotImplementedError(
627 'Running tests interactively (DEBUG, STEP or FORCE_FOREGROUND is '
628 'set) in parallel (TEST_JOBS is more than 1) is not '
629 'supported')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100630
Klement Sekera3f6ff192017-08-11 06:56:05 +0200631 parser = argparse.ArgumentParser(description="VPP unit tests")
juraj.linkes184870a2018-07-16 14:22:01 +0200632 parser.add_argument("-f", "--failfast", action='store_true',
Klement Sekera3f6ff192017-08-11 06:56:05 +0200633 help="fast failure flag")
634 parser.add_argument("-d", "--dir", action='append', type=str,
635 help="directory containing test files "
636 "(may be specified multiple times)")
637 args = parser.parse_args()
juraj.linkes184870a2018-07-16 14:22:01 +0200638 failfast = args.failfast
639 descriptions = True
Klement Sekera3f6ff192017-08-11 06:56:05 +0200640
juraj.linkes184870a2018-07-16 14:22:01 +0200641 print("Running tests using custom test runner") # debug message
642 filter_file, filter_class, filter_func = parse_test_option()
643
644 print("Active filters: file=%s, class=%s, function=%s" % (
645 filter_file, filter_class, filter_func))
646
647 filter_cb = FilterByTestOption(filter_file, filter_class, filter_func)
648
649 cb = SplitToSuitesCallback(filter_cb)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200650 for d in args.dir:
Klement Sekeradf2b9802017-10-05 10:26:03 +0200651 print("Adding tests from directory tree %s" % d)
Klement Sekerafcbf4442017-08-17 07:38:42 +0200652 discover_tests(d, cb)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200653
juraj.linkes184870a2018-07-16 14:22:01 +0200654 # suites are not hashable, need to use list
655 suites = []
656 tests_amount = 0
657 for testcase_suite in cb.suites.values():
658 tests_amount += testcase_suite.countTestCases()
659 suites.append(testcase_suite)
Klement Sekerabbfa5fd2018-06-27 13:54:32 +0200660
juraj.linkes184870a2018-07-16 14:22:01 +0200661 if concurrent_tests == 1:
662 new_suite = unittest.TestSuite()
663 for suite in suites:
juraj.linkes721872e2018-09-05 18:13:45 +0200664 new_suite.addTests(suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200665
666 suites = [new_suite]
667
668 print("%s out of %s tests match specified filters" % (
669 tests_amount, tests_amount + cb.filtered.countTestCases()))
670
671 if not running_extended_tests():
672 print("Not running extended tests (some tests will be skipped)")
673
Klement Sekeradf2b9802017-10-05 10:26:03 +0200674 attempts = retries + 1
675 if attempts > 1:
676 print("Perform %s attempts to pass the suite..." % attempts)
juraj.linkes184870a2018-07-16 14:22:01 +0200677
678 if run_interactive:
679 # don't fork if requiring interactive terminal
Klement Sekera13a83ef2018-03-21 12:35:51 +0100680 sys.exit(not VppTestRunner(
juraj.linkes184870a2018-07-16 14:22:01 +0200681 verbosity=verbose, failfast=failfast)
682 .run(suites[0]).wasSuccessful())
Klement Sekera13a83ef2018-03-21 12:35:51 +0100683 else:
juraj.linkes184870a2018-07-16 14:22:01 +0200684 exit_code = 0
685 while len(suites) > 0 and attempts > 0:
686 tests_amount = sum([x.countTestCases() for x in suites])
687 results = run_forked(suites)
688 exit_code, suites = parse_results(results)
689 attempts -= 1
690 if exit_code == 0:
691 print('Test run was successful')
692 else:
693 print('%s attempt(s) left.' % attempts)
694 sys.exit(exit_code)