blob: 0cd057ca0c33110606089e33fce559e2f8cb99ac [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, \
juraj.linkescae64f82018-09-19 15:01:47 +020017 get_testcase_doc_name, get_test_description, PASS, FAIL, ERROR, SKIP, \
18 TEST_RUN
Klement Sekera909a6a12017-08-08 04:33:53 +020019from debug import spawn_gdb
juraj.linkes184870a2018-07-16 14:22:01 +020020from log import get_parallel_logger, double_line_delim, RED, YELLOW, GREEN, \
21 colorize
Klement Sekerafcbf4442017-08-17 07:38:42 +020022from discover_tests import discover_tests
Klement Sekera9b6ece72018-03-23 10:50:11 +010023from subprocess import check_output, CalledProcessError
Andrew Yourtchenko57612eb2018-03-28 15:32:10 +020024from util import check_core_path
Klement Sekera993e0ed2017-03-16 09:14:59 +010025
Klement Sekera05742262018-03-14 18:14:49 +010026# timeout which controls how long the child has to finish after seeing
27# a core dump in test temporary directory. If this is exceeded, parent assumes
28# that child process is stuck (e.g. waiting for shm mutex, which will never
29# get unlocked) and kill the child
30core_timeout = 3
juraj.linkes184870a2018-07-16 14:22:01 +020031min_req_shm = 536870912 # min 512MB shm required
32# 128MB per extra process
33shm_per_process = 134217728
Klement Sekera05742262018-03-14 18:14:49 +010034
Klement Sekera909a6a12017-08-08 04:33:53 +020035
juraj.linkes184870a2018-07-16 14:22:01 +020036class StreamQueue(Queue):
37 def write(self, msg):
38 self.put(msg)
39
40 def flush(self):
41 sys.__stdout__.flush()
42 sys.__stderr__.flush()
43
44 def fileno(self):
45 return self._writer.fileno()
46
47
48class StreamQueueManager(BaseManager):
49 pass
50
51
juraj.linkescae64f82018-09-19 15:01:47 +020052StreamQueueManager.register('StreamQueue', StreamQueue)
juraj.linkes184870a2018-07-16 14:22:01 +020053
54
juraj.linkescae64f82018-09-19 15:01:47 +020055class TestResult(dict):
56 def __init__(self, testcase_suite):
57 super(TestResult, self).__init__()
58 self[PASS] = []
59 self[FAIL] = []
60 self[ERROR] = []
61 self[SKIP] = []
62 self[TEST_RUN] = []
63 self.testcase_suite = testcase_suite
64 self.testcases = [testcase for testcase in testcase_suite]
65 self.testcases_by_id = {}
66
67 def was_successful(self):
68 return len(self[PASS] + self[SKIP]) \
69 == self.testcase_suite.countTestCases()
70
71 def no_tests_run(self):
72 return 0 == len(self[TEST_RUN])
73
74 def process_result(self, test_id, result):
75 self[result].append(test_id)
76 for testcase in self.testcases:
77 if testcase.id() == test_id:
78 self.testcases_by_id[test_id] = testcase
79 self.testcases.remove(testcase)
80 break
81
82 def suite_from_failed(self):
83 rerun_ids = set([])
84 for testcase in self.testcase_suite:
85 tc_id = testcase.id()
86 if tc_id not in self[PASS] and tc_id not in self[SKIP]:
87 rerun_ids.add(tc_id)
88 if len(rerun_ids) > 0:
89 return suite_from_failed(self.testcase_suite, rerun_ids)
90
91 def get_testcase_names(self, test_id):
92 return self._get_testcase_class(test_id), \
93 self._get_test_description(test_id)
94
95 def _get_test_description(self, test_id):
96 if test_id in self.testcases_by_id:
97 return get_test_description(descriptions,
98 self.testcases_by_id[test_id])
99 else:
100 return test_id
101
102 def _get_testcase_class(self, test_id):
103 if test_id in self.testcases_by_id:
104 return get_testcase_doc_name(self.testcases_by_id[test_id])
105 else:
106 return test_id
107
108
109def test_runner_wrapper(suite, keep_alive_pipe, stdouterr_queue,
110 finished_pipe, result_pipe, logger):
juraj.linkes184870a2018-07-16 14:22:01 +0200111 sys.stdout = stdouterr_queue
112 sys.stderr = stdouterr_queue
113 VppTestCase.logger = logger
114 result = VppTestRunner(keep_alive_pipe=keep_alive_pipe,
115 descriptions=descriptions,
116 verbosity=verbose,
juraj.linkescae64f82018-09-19 15:01:47 +0200117 result_pipe=result_pipe,
juraj.linkes184870a2018-07-16 14:22:01 +0200118 failfast=failfast).run(suite)
juraj.linkescae64f82018-09-19 15:01:47 +0200119 finished_pipe.send(result.wasSuccessful())
120 finished_pipe.close()
Klement Sekera909a6a12017-08-08 04:33:53 +0200121 keep_alive_pipe.close()
122
123
juraj.linkes184870a2018-07-16 14:22:01 +0200124class TestCaseWrapper(object):
125 def __init__(self, testcase_suite, manager):
126 self.keep_alive_parent_end, self.keep_alive_child_end = Pipe(
127 duplex=False)
juraj.linkescae64f82018-09-19 15:01:47 +0200128 self.finished_parent_end, self.finished_child_end = Pipe(duplex=False)
juraj.linkes184870a2018-07-16 14:22:01 +0200129 self.result_parent_end, self.result_child_end = Pipe(duplex=False)
130 self.testcase_suite = testcase_suite
juraj.linkescae64f82018-09-19 15:01:47 +0200131 self.stdouterr_queue = manager.StreamQueue()
juraj.linkes184870a2018-07-16 14:22:01 +0200132 self.logger = get_parallel_logger(self.stdouterr_queue)
133 self.child = Process(target=test_runner_wrapper,
juraj.linkescae64f82018-09-19 15:01:47 +0200134 args=(testcase_suite,
135 self.keep_alive_child_end,
136 self.stdouterr_queue,
137 self.finished_child_end,
138 self.result_child_end,
139 self.logger)
juraj.linkes184870a2018-07-16 14:22:01 +0200140 )
141 self.child.start()
juraj.linkes184870a2018-07-16 14:22:01 +0200142 self.last_test_temp_dir = None
143 self.last_test_vpp_binary = None
144 self.last_test = None
145 self.result = None
juraj.linkes721872e2018-09-05 18:13:45 +0200146 self.vpp_pid = None
juraj.linkes184870a2018-07-16 14:22:01 +0200147 self.last_heard = time.time()
148 self.core_detected_at = None
juraj.linkescae64f82018-09-19 15:01:47 +0200149 self.result = TestResult(testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200150
151 def close_pipes(self):
152 self.keep_alive_child_end.close()
juraj.linkescae64f82018-09-19 15:01:47 +0200153 self.finished_child_end.close()
juraj.linkes184870a2018-07-16 14:22:01 +0200154 self.result_child_end.close()
155 self.keep_alive_parent_end.close()
juraj.linkescae64f82018-09-19 15:01:47 +0200156 self.finished_parent_end.close()
juraj.linkes184870a2018-07-16 14:22:01 +0200157 self.result_parent_end.close()
158
159
160def stdouterr_reader_wrapper(unread_testcases, finished_unread_testcases,
161 read_testcases):
162 read_testcase = None
163 while read_testcases.is_set() or len(unread_testcases) > 0:
164 if not read_testcase:
165 if len(finished_unread_testcases) > 0:
166 read_testcase = finished_unread_testcases.pop()
167 unread_testcases.remove(read_testcase)
168 elif len(unread_testcases) > 0:
169 read_testcase = unread_testcases.pop()
170 if read_testcase:
171 data = ''
172 while data is not None:
173 sys.stdout.write(data)
174 data = read_testcase.stdouterr_queue.get()
175
176 read_testcase.stdouterr_queue.close()
177 finished_unread_testcases.discard(read_testcase)
178 read_testcase = None
179
180
juraj.linkes721872e2018-09-05 18:13:45 +0200181def run_forked(testcase_suites):
juraj.linkes184870a2018-07-16 14:22:01 +0200182 wrapped_testcase_suites = set()
183
184 # suites are unhashable, need to use list
185 results = []
186 debug_core = os.getenv("DEBUG", "").lower() == "core"
187 unread_testcases = set()
188 finished_unread_testcases = set()
189 manager = StreamQueueManager()
190 manager.start()
191 for i in range(concurrent_tests):
juraj.linkes721872e2018-09-05 18:13:45 +0200192 if len(testcase_suites) > 0:
193 wrapped_testcase_suite = TestCaseWrapper(testcase_suites.pop(0),
194 manager)
juraj.linkes184870a2018-07-16 14:22:01 +0200195 wrapped_testcase_suites.add(wrapped_testcase_suite)
196 unread_testcases.add(wrapped_testcase_suite)
197 # time.sleep(1)
198 else:
199 break
200
201 read_from_testcases = threading.Event()
202 read_from_testcases.set()
203 stdouterr_thread = threading.Thread(target=stdouterr_reader_wrapper,
204 args=(unread_testcases,
205 finished_unread_testcases,
206 read_from_testcases))
207 stdouterr_thread.start()
208
209 while len(wrapped_testcase_suites) > 0:
210 finished_testcase_suites = set()
211 for wrapped_testcase_suite in wrapped_testcase_suites:
juraj.linkescae64f82018-09-19 15:01:47 +0200212 while wrapped_testcase_suite.result_parent_end.poll():
213 wrapped_testcase_suite.result.process_result(
214 *wrapped_testcase_suite.result_parent_end.recv())
215 wrapped_testcase_suite.last_heard = time.time()
216
217 if wrapped_testcase_suite.finished_parent_end.poll():
218 wrapped_testcase_suite.finished_parent_end.recv()
219 results.append(wrapped_testcase_suite.result)
juraj.linkes184870a2018-07-16 14:22:01 +0200220 finished_testcase_suites.add(wrapped_testcase_suite)
221 continue
222
juraj.linkescae64f82018-09-19 15:01:47 +0200223 while wrapped_testcase_suite.keep_alive_parent_end.poll():
224 wrapped_testcase_suite.last_test, \
225 wrapped_testcase_suite.last_test_vpp_binary, \
226 wrapped_testcase_suite.last_test_temp_dir, \
227 wrapped_testcase_suite.vpp_pid = \
228 wrapped_testcase_suite.keep_alive_parent_end.recv()
juraj.linkes184870a2018-07-16 14:22:01 +0200229 wrapped_testcase_suite.last_heard = time.time()
230
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200231 fail = False
232 if wrapped_testcase_suite.last_heard + test_timeout < time.time() \
233 and not os.path.isfile(
234 "%s/_core_handled" %
235 wrapped_testcase_suite.last_test_temp_dir):
236 fail = True
237 wrapped_testcase_suite.logger.critical(
238 "Timeout while waiting for child test "
239 "runner process (last test running was "
240 "`%s' in `%s')!" %
241 (wrapped_testcase_suite.last_test,
242 wrapped_testcase_suite.last_test_temp_dir))
243 elif not wrapped_testcase_suite.child.is_alive():
244 fail = True
245 wrapped_testcase_suite.logger.critical(
246 "Child python process unexpectedly died "
247 "(last test running was `%s' in `%s')!" %
248 (wrapped_testcase_suite.last_test,
249 wrapped_testcase_suite.last_test_temp_dir))
250 elif wrapped_testcase_suite.last_test_temp_dir and \
251 wrapped_testcase_suite.last_test_vpp_binary:
252 core_path = "%s/core" % \
253 wrapped_testcase_suite.last_test_temp_dir
254 if os.path.isfile(core_path):
255 if wrapped_testcase_suite.core_detected_at is None:
256 wrapped_testcase_suite.core_detected_at = time.time()
257 elif wrapped_testcase_suite.core_detected_at + \
258 core_timeout < time.time():
259 if not os.path.isfile(
260 "%s/_core_handled" %
261 wrapped_testcase_suite.
262 last_test_temp_dir):
263 wrapped_testcase_suite.logger.critical(
264 "Child python process unresponsive and core-"
265 "file exists in test temporary directory!")
266 fail = True
juraj.linkes184870a2018-07-16 14:22:01 +0200267
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200268 if fail:
juraj.linkes184870a2018-07-16 14:22:01 +0200269 failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
juraj.linkes721872e2018-09-05 18:13:45 +0200270 if wrapped_testcase_suite.last_test_temp_dir:
271 lttd = os.path.basename(
272 wrapped_testcase_suite.last_test_temp_dir)
273 else:
274 lttd = None
juraj.linkes184870a2018-07-16 14:22:01 +0200275 link_path = '%s%s-FAILED' % (failed_dir, lttd)
276 wrapped_testcase_suite.logger.error(
277 "Creating a link to the failed test: %s -> %s" %
278 (link_path, lttd))
juraj.linkes721872e2018-09-05 18:13:45 +0200279 if not os.path.exists(link_path) \
280 and wrapped_testcase_suite.last_test_temp_dir:
juraj.linkes184870a2018-07-16 14:22:01 +0200281 os.symlink(wrapped_testcase_suite.last_test_temp_dir,
282 link_path)
283 api_post_mortem_path = "/tmp/api_post_mortem.%d" % \
284 wrapped_testcase_suite.vpp_pid
285 if os.path.isfile(api_post_mortem_path):
286 wrapped_testcase_suite.logger.error(
287 "Copying api_post_mortem.%d to %s" %
288 (wrapped_testcase_suite.vpp_pid,
289 wrapped_testcase_suite.last_test_temp_dir))
290 shutil.copy2(api_post_mortem_path,
291 wrapped_testcase_suite.last_test_temp_dir)
292 if wrapped_testcase_suite.last_test_temp_dir and \
293 wrapped_testcase_suite.last_test_vpp_binary:
294 core_path = "%s/core" % \
295 wrapped_testcase_suite.last_test_temp_dir
296 if os.path.isfile(core_path):
297 wrapped_testcase_suite.logger.error(
298 "Core-file exists in test temporary directory: %s!"
299 % core_path)
300 check_core_path(wrapped_testcase_suite.logger,
301 core_path)
302 wrapped_testcase_suite.logger.debug(
303 "Running `file %s':" % core_path)
304 try:
305 info = check_output(["file", core_path])
306 wrapped_testcase_suite.logger.debug(info)
307 except CalledProcessError as e:
308 wrapped_testcase_suite.logger.error(
309 "Could not run `file' utility on core-file, "
310 "rc=%s" % e.returncode)
311 pass
312 if debug_core:
313 spawn_gdb(
314 wrapped_testcase_suite.last_test_vpp_binary,
315 core_path, wrapped_testcase_suite.logger)
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200316 wrapped_testcase_suite.child.terminate()
juraj.linkes184870a2018-07-16 14:22:01 +0200317 try:
318 # terminating the child process tends to leave orphan
319 # VPP process around
320 os.kill(wrapped_testcase_suite.vpp_pid, signal.SIGTERM)
321 except OSError:
322 # already dead
323 pass
juraj.linkescae64f82018-09-19 15:01:47 +0200324 results.append(wrapped_testcase_suite.result)
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200325 finished_testcase_suites.add(wrapped_testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200326
327 for finished_testcase in finished_testcase_suites:
328 finished_testcase.child.join()
329 finished_testcase.close_pipes()
330 wrapped_testcase_suites.remove(finished_testcase)
331 finished_unread_testcases.add(finished_testcase)
332 finished_testcase.stdouterr_queue.put(None)
juraj.linkes721872e2018-09-05 18:13:45 +0200333 if len(testcase_suites) > 0:
334 new_testcase = TestCaseWrapper(testcase_suites.pop(0), manager)
juraj.linkes184870a2018-07-16 14:22:01 +0200335 wrapped_testcase_suites.add(new_testcase)
336 unread_testcases.add(new_testcase)
337
338 read_from_testcases.clear()
339 stdouterr_thread.join(test_timeout)
340 manager.shutdown()
341 return results
342
343
344class SplitToSuitesCallback:
345 def __init__(self, filter_callback):
346 self.suites = {}
347 self.suite_name = 'default'
348 self.filter_callback = filter_callback
349 self.filtered = unittest.TestSuite()
Klement Sekerafcbf4442017-08-17 07:38:42 +0200350
351 def __call__(self, file_name, cls, method):
juraj.linkes184870a2018-07-16 14:22:01 +0200352 test_method = cls(method)
353 if self.filter_callback(file_name, cls.__name__, method):
354 self.suite_name = file_name + cls.__name__
355 if self.suite_name not in self.suites:
356 self.suites[self.suite_name] = unittest.TestSuite()
357 self.suites[self.suite_name].addTest(test_method)
358
359 else:
360 self.filtered.addTest(test_method)
Klement Sekerafcbf4442017-08-17 07:38:42 +0200361
362
juraj.linkes184870a2018-07-16 14:22:01 +0200363test_option = "TEST"
364
365
366def parse_test_option():
367 f = os.getenv(test_option, None)
368 filter_file_name = None
369 filter_class_name = None
370 filter_func_name = None
371 if f:
372 if '.' in f:
373 parts = f.split('.')
374 if len(parts) > 3:
375 raise Exception("Unrecognized %s option: %s" %
376 (test_option, f))
377 if len(parts) > 2:
378 if parts[2] not in ('*', ''):
379 filter_func_name = parts[2]
380 if parts[1] not in ('*', ''):
381 filter_class_name = parts[1]
382 if parts[0] not in ('*', ''):
383 if parts[0].startswith('test_'):
384 filter_file_name = parts[0]
385 else:
386 filter_file_name = 'test_%s' % parts[0]
387 else:
388 if f.startswith('test_'):
389 filter_file_name = f
390 else:
391 filter_file_name = 'test_%s' % f
392 if filter_file_name:
393 filter_file_name = '%s.py' % filter_file_name
394 return filter_file_name, filter_class_name, filter_func_name
395
396
397def filter_tests(tests, filter_cb):
398 result = unittest.suite.TestSuite()
399 for t in tests:
400 if isinstance(t, unittest.suite.TestSuite):
401 # this is a bunch of tests, recursively filter...
402 x = filter_tests(t, filter_cb)
403 if x.countTestCases() > 0:
404 result.addTest(x)
405 elif isinstance(t, unittest.TestCase):
406 # this is a single test
407 parts = t.id().split('.')
408 # t.id() for common cases like this:
409 # test_classifier.TestClassifier.test_acl_ip
410 # apply filtering only if it is so
411 if len(parts) == 3:
412 if not filter_cb(parts[0], parts[1], parts[2]):
413 continue
414 result.addTest(t)
415 else:
416 # unexpected object, don't touch it
417 result.addTest(t)
418 return result
419
420
421class FilterByTestOption:
422 def __init__(self, filter_file_name, filter_class_name, filter_func_name):
423 self.filter_file_name = filter_file_name
424 self.filter_class_name = filter_class_name
425 self.filter_func_name = filter_func_name
426
427 def __call__(self, file_name, class_name, func_name):
428 if self.filter_file_name and file_name != self.filter_file_name:
429 return False
430 if self.filter_class_name and class_name != self.filter_class_name:
431 return False
432 if self.filter_func_name and func_name != self.filter_func_name:
433 return False
434 return True
435
436
437class FilterByClassList:
juraj.linkes721872e2018-09-05 18:13:45 +0200438 def __init__(self, classes_with_filenames):
439 self.classes_with_filenames = classes_with_filenames
Klement Sekeradf2b9802017-10-05 10:26:03 +0200440
441 def __call__(self, file_name, class_name, func_name):
juraj.linkes721872e2018-09-05 18:13:45 +0200442 return '.'.join([file_name, class_name]) in self.classes_with_filenames
Klement Sekeradf2b9802017-10-05 10:26:03 +0200443
444
445def suite_from_failed(suite, failed):
juraj.linkes721872e2018-09-05 18:13:45 +0200446 failed = {x.rsplit('.', 1)[0] for x in failed}
juraj.linkes184870a2018-07-16 14:22:01 +0200447 filter_cb = FilterByClassList(failed)
448 suite = filter_tests(suite, filter_cb)
Klement Sekera4c5422e2018-06-22 13:19:45 +0200449 return suite
Klement Sekeradf2b9802017-10-05 10:26:03 +0200450
451
juraj.linkescae64f82018-09-19 15:01:47 +0200452class AllResults(dict):
juraj.linkes184870a2018-07-16 14:22:01 +0200453 def __init__(self):
juraj.linkescae64f82018-09-19 15:01:47 +0200454 super(AllResults, self).__init__()
juraj.linkes184870a2018-07-16 14:22:01 +0200455 self.all_testcases = 0
juraj.linkescae64f82018-09-19 15:01:47 +0200456 self.results_per_suite = []
457 self[PASS] = 0
458 self[FAIL] = 0
459 self[ERROR] = 0
460 self[SKIP] = 0
461 self[TEST_RUN] = 0
juraj.linkes184870a2018-07-16 14:22:01 +0200462 self.rerun = []
juraj.linkescae64f82018-09-19 15:01:47 +0200463 self.testsuites_no_tests_run = []
Klement Sekera909a6a12017-08-08 04:33:53 +0200464
juraj.linkescae64f82018-09-19 15:01:47 +0200465 def add_results(self, result):
466 self.results_per_suite.append(result)
467 result_types = [PASS, FAIL, ERROR, SKIP, TEST_RUN]
468 for result_type in result_types:
469 self[result_type] += len(result[result_type])
Klement Sekera05742262018-03-14 18:14:49 +0100470
juraj.linkescae64f82018-09-19 15:01:47 +0200471 def add_result(self, result):
juraj.linkes184870a2018-07-16 14:22:01 +0200472 retval = 0
juraj.linkescae64f82018-09-19 15:01:47 +0200473 self.all_testcases += result.testcase_suite.countTestCases()
474 if not result.no_tests_run():
475 if not result.was_successful():
juraj.linkes184870a2018-07-16 14:22:01 +0200476 retval = 1
477
juraj.linkescae64f82018-09-19 15:01:47 +0200478 self.add_results(result)
juraj.linkes721872e2018-09-05 18:13:45 +0200479 else:
juraj.linkescae64f82018-09-19 15:01:47 +0200480 self.testsuites_no_tests_run.append(result.testcase_suite)
juraj.linkes721872e2018-09-05 18:13:45 +0200481 retval = -1
juraj.linkes184870a2018-07-16 14:22:01 +0200482
juraj.linkes184870a2018-07-16 14:22:01 +0200483 if retval != 0:
484 if concurrent_tests == 1:
juraj.linkescae64f82018-09-19 15:01:47 +0200485 if not result.no_tests_run():
486 self.rerun.append(result.suite_from_failed())
juraj.linkes184870a2018-07-16 14:22:01 +0200487 else:
juraj.linkescae64f82018-09-19 15:01:47 +0200488 self.rerun.append(result.testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200489 else:
juraj.linkescae64f82018-09-19 15:01:47 +0200490 self.rerun.append(result.testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200491
492 return retval
493
494 def print_results(self):
495 print('')
496 print(double_line_delim)
497 print('TEST RESULTS:')
juraj.linkescae64f82018-09-19 15:01:47 +0200498 print(' Scheduled tests: {}'.format(self.all_testcases))
499 print(' Executed tests: {}'.format(self[TEST_RUN]))
500 print(' Passed tests: {}'.format(
501 colorize(str(self[PASS]), GREEN)))
502 if self[SKIP] > 0:
503 print(' Skipped tests: {}'.format(
504 colorize(str(self[SKIP]), YELLOW)))
505 if self.not_executed > 0:
506 print(' Not Executed tests: {}'.format(
507 colorize(str(self.not_executed), RED)))
508 if self[FAIL] > 0:
509 print(' Failures: {}'.format(
510 colorize(str(self[FAIL]), RED)))
511 if self[ERROR] > 0:
512 print(' Errors: {}'.format(
513 colorize(str(self[ERROR]), RED)))
juraj.linkes184870a2018-07-16 14:22:01 +0200514
515 if self.all_failed > 0:
516 print('FAILED TESTS:')
juraj.linkescae64f82018-09-19 15:01:47 +0200517 for result in self.results_per_suite:
518 failed_testcase_ids = result[FAIL]
519 errored_testcase_ids = result[ERROR]
520 old_testcase_name = None
521 if len(failed_testcase_ids) or len(errored_testcase_ids):
522 for failed_test_id in failed_testcase_ids:
523 new_testcase_name, test_name = \
524 result.get_testcase_names(failed_test_id)
525 if new_testcase_name != old_testcase_name:
526 print(' Testcase name: {}'.format(
527 colorize(new_testcase_name, RED)))
528 old_testcase_name = new_testcase_name
juraj.linkes184870a2018-07-16 14:22:01 +0200529 print(' FAILED: {}'.format(
juraj.linkescae64f82018-09-19 15:01:47 +0200530 colorize(test_name, RED)))
531 for failed_test_id in errored_testcase_ids:
532 new_testcase_name, test_name = \
533 result.get_testcase_names(failed_test_id)
534 if new_testcase_name != old_testcase_name:
535 print(' Testcase name: {}'.format(
536 colorize(new_testcase_name, RED)))
537 old_testcase_name = new_testcase_name
juraj.linkes184870a2018-07-16 14:22:01 +0200538 print(' ERRORED: {}'.format(
juraj.linkescae64f82018-09-19 15:01:47 +0200539 colorize(test_name, RED)))
540 if len(self.testsuites_no_tests_run) > 0:
541 print('TESTCASES WHERE NO TESTS WERE SUCCESSFULLY EXECUTED:')
542 tc_classes = set([])
543 for testsuite in self.testsuites_no_tests_run:
544 for testcase in testsuite:
545 tc_classes.add(get_testcase_doc_name(testcase))
546 for tc_class in tc_classes:
547 print(' {}'.format(colorize(tc_class, RED)))
juraj.linkes184870a2018-07-16 14:22:01 +0200548
549 print(double_line_delim)
550 print('')
551
552 @property
juraj.linkescae64f82018-09-19 15:01:47 +0200553 def not_executed(self):
554 return self.all_testcases - self[TEST_RUN]
555
556 @property
juraj.linkes184870a2018-07-16 14:22:01 +0200557 def all_failed(self):
juraj.linkescae64f82018-09-19 15:01:47 +0200558 return self[FAIL] + self[ERROR]
juraj.linkes184870a2018-07-16 14:22:01 +0200559
560
561def parse_results(results):
562 """
juraj.linkescae64f82018-09-19 15:01:47 +0200563 Prints the number of scheduled, executed, not executed, passed, failed,
564 errored and skipped tests and details about failed and errored tests.
juraj.linkes184870a2018-07-16 14:22:01 +0200565
juraj.linkescae64f82018-09-19 15:01:47 +0200566 Also returns all suites where any test failed.
juraj.linkes184870a2018-07-16 14:22:01 +0200567
568 :param results:
569 :return:
570 """
571
juraj.linkescae64f82018-09-19 15:01:47 +0200572 results_per_suite = AllResults()
juraj.linkes184870a2018-07-16 14:22:01 +0200573 crashed = False
574 failed = False
juraj.linkescae64f82018-09-19 15:01:47 +0200575 for result in results:
576 result_code = results_per_suite.add_result(result)
juraj.linkes184870a2018-07-16 14:22:01 +0200577 if result_code == 1:
578 failed = True
579 elif result_code == -1:
580 crashed = True
581
582 results_per_suite.print_results()
583
584 if crashed:
585 return_code = -1
586 elif failed:
587 return_code = 1
588 else:
589 return_code = 0
590 return return_code, results_per_suite.rerun
591
592
593def parse_digit_env(env_var, default):
594 value = os.getenv(env_var, default)
595 if value != default:
596 if value.isdigit():
597 value = int(value)
598 else:
599 print('WARNING: unsupported value "%s" for env var "%s",'
600 'defaulting to %s' % (value, env_var, default))
601 value = default
602 return value
Klement Sekera3f6ff192017-08-11 06:56:05 +0200603
604
605if __name__ == '__main__':
606
juraj.linkes184870a2018-07-16 14:22:01 +0200607 verbose = parse_digit_env("V", 0)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200608
juraj.linkes184870a2018-07-16 14:22:01 +0200609 test_timeout = parse_digit_env("TIMEOUT", 600) # default = 10 minutes
Klement Sekera3f6ff192017-08-11 06:56:05 +0200610
juraj.linkes184870a2018-07-16 14:22:01 +0200611 retries = parse_digit_env("RETRIES", 0)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200612
juraj.linkes184870a2018-07-16 14:22:01 +0200613 debug = os.getenv("DEBUG", "n").lower() in ["gdb", "gdbserver"]
614
615 step = os.getenv("STEP", "n").lower() in ("y", "yes", "1")
616
617 force_foreground = \
618 os.getenv("FORCE_FOREGROUND", "n").lower() in ("y", "yes", "1")
619
620 run_interactive = debug or step or force_foreground
621
622 test_jobs = os.getenv("TEST_JOBS", "1").lower() # default = 1 process
623 if test_jobs == 'auto':
624 if run_interactive:
625 concurrent_tests = 1
626 print('Interactive mode required, running on one core')
627 else:
628 shm_free = psutil.disk_usage('/dev/shm').free
629 shm_max_processes = 1
630 if shm_free < min_req_shm:
631 raise Exception('Not enough free space in /dev/shm. Required '
632 'free space is at least %sM.'
633 % (min_req_shm >> 20))
634 else:
635 extra_shm = shm_free - min_req_shm
636 shm_max_processes += extra_shm / shm_per_process
Klement Sekera6c7bda92018-09-18 14:32:27 +0200637 concurrent_tests = min(cpu_count(), shm_max_processes)
juraj.linkes184870a2018-07-16 14:22:01 +0200638 print('Found enough resources to run tests with %s cores'
639 % concurrent_tests)
640 elif test_jobs.isdigit():
641 concurrent_tests = int(test_jobs)
642 else:
643 concurrent_tests = 1
644
645 if run_interactive and concurrent_tests > 1:
646 raise NotImplementedError(
647 'Running tests interactively (DEBUG, STEP or FORCE_FOREGROUND is '
648 'set) in parallel (TEST_JOBS is more than 1) is not '
649 'supported')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100650
Klement Sekera3f6ff192017-08-11 06:56:05 +0200651 parser = argparse.ArgumentParser(description="VPP unit tests")
juraj.linkes184870a2018-07-16 14:22:01 +0200652 parser.add_argument("-f", "--failfast", action='store_true',
Klement Sekera3f6ff192017-08-11 06:56:05 +0200653 help="fast failure flag")
654 parser.add_argument("-d", "--dir", action='append', type=str,
655 help="directory containing test files "
656 "(may be specified multiple times)")
657 args = parser.parse_args()
juraj.linkes184870a2018-07-16 14:22:01 +0200658 failfast = args.failfast
659 descriptions = True
Klement Sekera3f6ff192017-08-11 06:56:05 +0200660
juraj.linkes184870a2018-07-16 14:22:01 +0200661 print("Running tests using custom test runner") # debug message
662 filter_file, filter_class, filter_func = parse_test_option()
663
664 print("Active filters: file=%s, class=%s, function=%s" % (
665 filter_file, filter_class, filter_func))
666
667 filter_cb = FilterByTestOption(filter_file, filter_class, filter_func)
668
669 cb = SplitToSuitesCallback(filter_cb)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200670 for d in args.dir:
Klement Sekeradf2b9802017-10-05 10:26:03 +0200671 print("Adding tests from directory tree %s" % d)
Klement Sekerafcbf4442017-08-17 07:38:42 +0200672 discover_tests(d, cb)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200673
juraj.linkes184870a2018-07-16 14:22:01 +0200674 # suites are not hashable, need to use list
675 suites = []
676 tests_amount = 0
677 for testcase_suite in cb.suites.values():
678 tests_amount += testcase_suite.countTestCases()
679 suites.append(testcase_suite)
Klement Sekerabbfa5fd2018-06-27 13:54:32 +0200680
juraj.linkes184870a2018-07-16 14:22:01 +0200681 if concurrent_tests == 1:
682 new_suite = unittest.TestSuite()
683 for suite in suites:
juraj.linkes721872e2018-09-05 18:13:45 +0200684 new_suite.addTests(suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200685
686 suites = [new_suite]
687
688 print("%s out of %s tests match specified filters" % (
689 tests_amount, tests_amount + cb.filtered.countTestCases()))
690
691 if not running_extended_tests():
692 print("Not running extended tests (some tests will be skipped)")
693
Klement Sekeradf2b9802017-10-05 10:26:03 +0200694 attempts = retries + 1
695 if attempts > 1:
696 print("Perform %s attempts to pass the suite..." % attempts)
juraj.linkes184870a2018-07-16 14:22:01 +0200697
698 if run_interactive:
699 # don't fork if requiring interactive terminal
Klement Sekera13a83ef2018-03-21 12:35:51 +0100700 sys.exit(not VppTestRunner(
juraj.linkes184870a2018-07-16 14:22:01 +0200701 verbosity=verbose, failfast=failfast)
702 .run(suites[0]).wasSuccessful())
Klement Sekera13a83ef2018-03-21 12:35:51 +0100703 else:
juraj.linkes184870a2018-07-16 14:22:01 +0200704 exit_code = 0
705 while len(suites) > 0 and attempts > 0:
706 tests_amount = sum([x.countTestCases() for x in suites])
707 results = run_forked(suites)
708 exit_code, suites = parse_results(results)
709 attempts -= 1
710 if exit_code == 0:
711 print('Test run was successful')
712 else:
713 print('%s attempt(s) left.' % attempts)
714 sys.exit(exit_code)