blob: 6836466373e988a4ee65d6869bc799ce7c017f49 [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
Andrew Yourtchenkod760f792018-10-03 11:38:31 +02006import fnmatch
Klement Sekera909a6a12017-08-08 04:33:53 +02007import select
Damjan Marionf56b77a2016-10-03 19:44:57 +02008import unittest
Klement Sekera993e0ed2017-03-16 09:14:59 +01009import argparse
Klement Sekera545be522018-02-16 19:25:06 +010010import time
juraj.linkes184870a2018-07-16 14:22:01 +020011import threading
12import signal
13import psutil
14from multiprocessing import Process, Pipe, cpu_count
15from multiprocessing.queues import Queue
16from multiprocessing.managers import BaseManager
17from framework import VppTestRunner, running_extended_tests, VppTestCase, \
juraj.linkescae64f82018-09-19 15:01:47 +020018 get_testcase_doc_name, get_test_description, PASS, FAIL, ERROR, SKIP, \
19 TEST_RUN
Klement Sekera909a6a12017-08-08 04:33:53 +020020from debug import spawn_gdb
juraj.linkes184870a2018-07-16 14:22:01 +020021from log import get_parallel_logger, double_line_delim, RED, YELLOW, GREEN, \
22 colorize
Klement Sekerafcbf4442017-08-17 07:38:42 +020023from discover_tests import discover_tests
Klement Sekera9b6ece72018-03-23 10:50:11 +010024from subprocess import check_output, CalledProcessError
Andrew Yourtchenko57612eb2018-03-28 15:32:10 +020025from util import check_core_path
Klement Sekera993e0ed2017-03-16 09:14:59 +010026
Klement Sekera05742262018-03-14 18:14:49 +010027# timeout which controls how long the child has to finish after seeing
28# a core dump in test temporary directory. If this is exceeded, parent assumes
29# that child process is stuck (e.g. waiting for shm mutex, which will never
30# get unlocked) and kill the child
31core_timeout = 3
juraj.linkes184870a2018-07-16 14:22:01 +020032min_req_shm = 536870912 # min 512MB shm required
33# 128MB per extra process
34shm_per_process = 134217728
Klement Sekera05742262018-03-14 18:14:49 +010035
Klement Sekera909a6a12017-08-08 04:33:53 +020036
juraj.linkes184870a2018-07-16 14:22:01 +020037class StreamQueue(Queue):
38 def write(self, msg):
39 self.put(msg)
40
41 def flush(self):
42 sys.__stdout__.flush()
43 sys.__stderr__.flush()
44
45 def fileno(self):
46 return self._writer.fileno()
47
48
49class StreamQueueManager(BaseManager):
50 pass
51
52
juraj.linkescae64f82018-09-19 15:01:47 +020053StreamQueueManager.register('StreamQueue', StreamQueue)
juraj.linkes184870a2018-07-16 14:22:01 +020054
55
juraj.linkescae64f82018-09-19 15:01:47 +020056class TestResult(dict):
57 def __init__(self, testcase_suite):
58 super(TestResult, self).__init__()
59 self[PASS] = []
60 self[FAIL] = []
61 self[ERROR] = []
62 self[SKIP] = []
63 self[TEST_RUN] = []
64 self.testcase_suite = testcase_suite
65 self.testcases = [testcase for testcase in testcase_suite]
66 self.testcases_by_id = {}
67
68 def was_successful(self):
69 return len(self[PASS] + self[SKIP]) \
70 == self.testcase_suite.countTestCases()
71
72 def no_tests_run(self):
73 return 0 == len(self[TEST_RUN])
74
75 def process_result(self, test_id, result):
76 self[result].append(test_id)
77 for testcase in self.testcases:
78 if testcase.id() == test_id:
79 self.testcases_by_id[test_id] = testcase
80 self.testcases.remove(testcase)
81 break
82
83 def suite_from_failed(self):
84 rerun_ids = set([])
85 for testcase in self.testcase_suite:
86 tc_id = testcase.id()
87 if tc_id not in self[PASS] and tc_id not in self[SKIP]:
88 rerun_ids.add(tc_id)
89 if len(rerun_ids) > 0:
90 return suite_from_failed(self.testcase_suite, rerun_ids)
91
92 def get_testcase_names(self, test_id):
93 return self._get_testcase_class(test_id), \
94 self._get_test_description(test_id)
95
96 def _get_test_description(self, test_id):
97 if test_id in self.testcases_by_id:
98 return get_test_description(descriptions,
99 self.testcases_by_id[test_id])
100 else:
101 return test_id
102
103 def _get_testcase_class(self, test_id):
104 if test_id in self.testcases_by_id:
105 return get_testcase_doc_name(self.testcases_by_id[test_id])
106 else:
107 return test_id
108
109
110def test_runner_wrapper(suite, keep_alive_pipe, stdouterr_queue,
111 finished_pipe, result_pipe, logger):
juraj.linkes184870a2018-07-16 14:22:01 +0200112 sys.stdout = stdouterr_queue
113 sys.stderr = stdouterr_queue
114 VppTestCase.logger = logger
115 result = VppTestRunner(keep_alive_pipe=keep_alive_pipe,
116 descriptions=descriptions,
117 verbosity=verbose,
juraj.linkescae64f82018-09-19 15:01:47 +0200118 result_pipe=result_pipe,
juraj.linkes184870a2018-07-16 14:22:01 +0200119 failfast=failfast).run(suite)
juraj.linkescae64f82018-09-19 15:01:47 +0200120 finished_pipe.send(result.wasSuccessful())
121 finished_pipe.close()
Klement Sekera909a6a12017-08-08 04:33:53 +0200122 keep_alive_pipe.close()
123
124
juraj.linkes184870a2018-07-16 14:22:01 +0200125class TestCaseWrapper(object):
126 def __init__(self, testcase_suite, manager):
127 self.keep_alive_parent_end, self.keep_alive_child_end = Pipe(
128 duplex=False)
juraj.linkescae64f82018-09-19 15:01:47 +0200129 self.finished_parent_end, self.finished_child_end = Pipe(duplex=False)
juraj.linkes184870a2018-07-16 14:22:01 +0200130 self.result_parent_end, self.result_child_end = Pipe(duplex=False)
131 self.testcase_suite = testcase_suite
juraj.linkescae64f82018-09-19 15:01:47 +0200132 self.stdouterr_queue = manager.StreamQueue()
juraj.linkes184870a2018-07-16 14:22:01 +0200133 self.logger = get_parallel_logger(self.stdouterr_queue)
134 self.child = Process(target=test_runner_wrapper,
juraj.linkescae64f82018-09-19 15:01:47 +0200135 args=(testcase_suite,
136 self.keep_alive_child_end,
137 self.stdouterr_queue,
138 self.finished_child_end,
139 self.result_child_end,
140 self.logger)
juraj.linkes184870a2018-07-16 14:22:01 +0200141 )
142 self.child.start()
juraj.linkes184870a2018-07-16 14:22:01 +0200143 self.last_test_temp_dir = None
144 self.last_test_vpp_binary = None
145 self.last_test = None
146 self.result = None
juraj.linkes721872e2018-09-05 18:13:45 +0200147 self.vpp_pid = None
juraj.linkes184870a2018-07-16 14:22:01 +0200148 self.last_heard = time.time()
149 self.core_detected_at = None
juraj.linkescae64f82018-09-19 15:01:47 +0200150 self.result = TestResult(testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200151
152 def close_pipes(self):
153 self.keep_alive_child_end.close()
juraj.linkescae64f82018-09-19 15:01:47 +0200154 self.finished_child_end.close()
juraj.linkes184870a2018-07-16 14:22:01 +0200155 self.result_child_end.close()
156 self.keep_alive_parent_end.close()
juraj.linkescae64f82018-09-19 15:01:47 +0200157 self.finished_parent_end.close()
juraj.linkes184870a2018-07-16 14:22:01 +0200158 self.result_parent_end.close()
159
160
161def stdouterr_reader_wrapper(unread_testcases, finished_unread_testcases,
162 read_testcases):
163 read_testcase = None
164 while read_testcases.is_set() or len(unread_testcases) > 0:
165 if not read_testcase:
166 if len(finished_unread_testcases) > 0:
167 read_testcase = finished_unread_testcases.pop()
168 unread_testcases.remove(read_testcase)
169 elif len(unread_testcases) > 0:
170 read_testcase = unread_testcases.pop()
171 if read_testcase:
172 data = ''
173 while data is not None:
174 sys.stdout.write(data)
175 data = read_testcase.stdouterr_queue.get()
176
177 read_testcase.stdouterr_queue.close()
178 finished_unread_testcases.discard(read_testcase)
179 read_testcase = None
180
181
juraj.linkes721872e2018-09-05 18:13:45 +0200182def run_forked(testcase_suites):
juraj.linkes184870a2018-07-16 14:22:01 +0200183 wrapped_testcase_suites = set()
184
185 # suites are unhashable, need to use list
186 results = []
187 debug_core = os.getenv("DEBUG", "").lower() == "core"
188 unread_testcases = set()
189 finished_unread_testcases = set()
190 manager = StreamQueueManager()
191 manager.start()
192 for i in range(concurrent_tests):
juraj.linkes721872e2018-09-05 18:13:45 +0200193 if len(testcase_suites) > 0:
194 wrapped_testcase_suite = TestCaseWrapper(testcase_suites.pop(0),
195 manager)
juraj.linkes184870a2018-07-16 14:22:01 +0200196 wrapped_testcase_suites.add(wrapped_testcase_suite)
197 unread_testcases.add(wrapped_testcase_suite)
198 # time.sleep(1)
199 else:
200 break
201
202 read_from_testcases = threading.Event()
203 read_from_testcases.set()
204 stdouterr_thread = threading.Thread(target=stdouterr_reader_wrapper,
205 args=(unread_testcases,
206 finished_unread_testcases,
207 read_from_testcases))
208 stdouterr_thread.start()
209
210 while len(wrapped_testcase_suites) > 0:
211 finished_testcase_suites = set()
212 for wrapped_testcase_suite in wrapped_testcase_suites:
juraj.linkescae64f82018-09-19 15:01:47 +0200213 while wrapped_testcase_suite.result_parent_end.poll():
214 wrapped_testcase_suite.result.process_result(
215 *wrapped_testcase_suite.result_parent_end.recv())
216 wrapped_testcase_suite.last_heard = time.time()
217
218 if wrapped_testcase_suite.finished_parent_end.poll():
219 wrapped_testcase_suite.finished_parent_end.recv()
220 results.append(wrapped_testcase_suite.result)
juraj.linkes184870a2018-07-16 14:22:01 +0200221 finished_testcase_suites.add(wrapped_testcase_suite)
222 continue
223
juraj.linkescae64f82018-09-19 15:01:47 +0200224 while wrapped_testcase_suite.keep_alive_parent_end.poll():
225 wrapped_testcase_suite.last_test, \
226 wrapped_testcase_suite.last_test_vpp_binary, \
227 wrapped_testcase_suite.last_test_temp_dir, \
228 wrapped_testcase_suite.vpp_pid = \
229 wrapped_testcase_suite.keep_alive_parent_end.recv()
juraj.linkes184870a2018-07-16 14:22:01 +0200230 wrapped_testcase_suite.last_heard = time.time()
231
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200232 fail = False
233 if wrapped_testcase_suite.last_heard + test_timeout < time.time() \
234 and not os.path.isfile(
235 "%s/_core_handled" %
236 wrapped_testcase_suite.last_test_temp_dir):
237 fail = True
238 wrapped_testcase_suite.logger.critical(
239 "Timeout while waiting for child test "
240 "runner process (last test running was "
241 "`%s' in `%s')!" %
242 (wrapped_testcase_suite.last_test,
243 wrapped_testcase_suite.last_test_temp_dir))
244 elif not wrapped_testcase_suite.child.is_alive():
245 fail = True
246 wrapped_testcase_suite.logger.critical(
247 "Child python process unexpectedly died "
248 "(last test running was `%s' in `%s')!" %
249 (wrapped_testcase_suite.last_test,
250 wrapped_testcase_suite.last_test_temp_dir))
251 elif wrapped_testcase_suite.last_test_temp_dir and \
252 wrapped_testcase_suite.last_test_vpp_binary:
253 core_path = "%s/core" % \
254 wrapped_testcase_suite.last_test_temp_dir
255 if os.path.isfile(core_path):
256 if wrapped_testcase_suite.core_detected_at is None:
257 wrapped_testcase_suite.core_detected_at = time.time()
258 elif wrapped_testcase_suite.core_detected_at + \
259 core_timeout < time.time():
260 if not os.path.isfile(
261 "%s/_core_handled" %
262 wrapped_testcase_suite.
263 last_test_temp_dir):
264 wrapped_testcase_suite.logger.critical(
265 "Child python process unresponsive and core-"
266 "file exists in test temporary directory!")
267 fail = True
juraj.linkes184870a2018-07-16 14:22:01 +0200268
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200269 if fail:
juraj.linkes184870a2018-07-16 14:22:01 +0200270 failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
juraj.linkes721872e2018-09-05 18:13:45 +0200271 if wrapped_testcase_suite.last_test_temp_dir:
272 lttd = os.path.basename(
273 wrapped_testcase_suite.last_test_temp_dir)
274 else:
275 lttd = None
juraj.linkes184870a2018-07-16 14:22:01 +0200276 link_path = '%s%s-FAILED' % (failed_dir, lttd)
277 wrapped_testcase_suite.logger.error(
278 "Creating a link to the failed test: %s -> %s" %
279 (link_path, lttd))
juraj.linkes721872e2018-09-05 18:13:45 +0200280 if not os.path.exists(link_path) \
281 and wrapped_testcase_suite.last_test_temp_dir:
juraj.linkes184870a2018-07-16 14:22:01 +0200282 os.symlink(wrapped_testcase_suite.last_test_temp_dir,
283 link_path)
284 api_post_mortem_path = "/tmp/api_post_mortem.%d" % \
285 wrapped_testcase_suite.vpp_pid
286 if os.path.isfile(api_post_mortem_path):
287 wrapped_testcase_suite.logger.error(
288 "Copying api_post_mortem.%d to %s" %
289 (wrapped_testcase_suite.vpp_pid,
290 wrapped_testcase_suite.last_test_temp_dir))
291 shutil.copy2(api_post_mortem_path,
292 wrapped_testcase_suite.last_test_temp_dir)
293 if wrapped_testcase_suite.last_test_temp_dir and \
294 wrapped_testcase_suite.last_test_vpp_binary:
295 core_path = "%s/core" % \
296 wrapped_testcase_suite.last_test_temp_dir
297 if os.path.isfile(core_path):
298 wrapped_testcase_suite.logger.error(
299 "Core-file exists in test temporary directory: %s!"
300 % core_path)
301 check_core_path(wrapped_testcase_suite.logger,
302 core_path)
303 wrapped_testcase_suite.logger.debug(
304 "Running `file %s':" % core_path)
305 try:
306 info = check_output(["file", core_path])
307 wrapped_testcase_suite.logger.debug(info)
308 except CalledProcessError as e:
309 wrapped_testcase_suite.logger.error(
310 "Could not run `file' utility on core-file, "
311 "rc=%s" % e.returncode)
312 pass
313 if debug_core:
314 spawn_gdb(
315 wrapped_testcase_suite.last_test_vpp_binary,
316 core_path, wrapped_testcase_suite.logger)
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200317 wrapped_testcase_suite.child.terminate()
juraj.linkes184870a2018-07-16 14:22:01 +0200318 try:
319 # terminating the child process tends to leave orphan
320 # VPP process around
321 os.kill(wrapped_testcase_suite.vpp_pid, signal.SIGTERM)
322 except OSError:
323 # already dead
324 pass
juraj.linkescae64f82018-09-19 15:01:47 +0200325 results.append(wrapped_testcase_suite.result)
juraj.linkes5e2c54d2018-08-30 10:51:45 +0200326 finished_testcase_suites.add(wrapped_testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200327
328 for finished_testcase in finished_testcase_suites:
329 finished_testcase.child.join()
330 finished_testcase.close_pipes()
331 wrapped_testcase_suites.remove(finished_testcase)
332 finished_unread_testcases.add(finished_testcase)
333 finished_testcase.stdouterr_queue.put(None)
juraj.linkes721872e2018-09-05 18:13:45 +0200334 if len(testcase_suites) > 0:
335 new_testcase = TestCaseWrapper(testcase_suites.pop(0), manager)
juraj.linkes184870a2018-07-16 14:22:01 +0200336 wrapped_testcase_suites.add(new_testcase)
337 unread_testcases.add(new_testcase)
338
339 read_from_testcases.clear()
340 stdouterr_thread.join(test_timeout)
341 manager.shutdown()
342 return results
343
344
345class SplitToSuitesCallback:
346 def __init__(self, filter_callback):
347 self.suites = {}
348 self.suite_name = 'default'
349 self.filter_callback = filter_callback
350 self.filtered = unittest.TestSuite()
Klement Sekerafcbf4442017-08-17 07:38:42 +0200351
352 def __call__(self, file_name, cls, method):
juraj.linkes184870a2018-07-16 14:22:01 +0200353 test_method = cls(method)
354 if self.filter_callback(file_name, cls.__name__, method):
355 self.suite_name = file_name + cls.__name__
356 if self.suite_name not in self.suites:
357 self.suites[self.suite_name] = unittest.TestSuite()
358 self.suites[self.suite_name].addTest(test_method)
359
360 else:
361 self.filtered.addTest(test_method)
Klement Sekerafcbf4442017-08-17 07:38:42 +0200362
363
juraj.linkes184870a2018-07-16 14:22:01 +0200364test_option = "TEST"
365
366
367def parse_test_option():
368 f = os.getenv(test_option, None)
369 filter_file_name = None
370 filter_class_name = None
371 filter_func_name = None
372 if f:
373 if '.' in f:
374 parts = f.split('.')
375 if len(parts) > 3:
376 raise Exception("Unrecognized %s option: %s" %
377 (test_option, f))
378 if len(parts) > 2:
379 if parts[2] not in ('*', ''):
380 filter_func_name = parts[2]
381 if parts[1] not in ('*', ''):
382 filter_class_name = parts[1]
383 if parts[0] not in ('*', ''):
384 if parts[0].startswith('test_'):
385 filter_file_name = parts[0]
386 else:
387 filter_file_name = 'test_%s' % parts[0]
388 else:
389 if f.startswith('test_'):
390 filter_file_name = f
391 else:
392 filter_file_name = 'test_%s' % f
393 if filter_file_name:
394 filter_file_name = '%s.py' % filter_file_name
395 return filter_file_name, filter_class_name, filter_func_name
396
397
398def filter_tests(tests, filter_cb):
399 result = unittest.suite.TestSuite()
400 for t in tests:
401 if isinstance(t, unittest.suite.TestSuite):
402 # this is a bunch of tests, recursively filter...
403 x = filter_tests(t, filter_cb)
404 if x.countTestCases() > 0:
405 result.addTest(x)
406 elif isinstance(t, unittest.TestCase):
407 # this is a single test
408 parts = t.id().split('.')
409 # t.id() for common cases like this:
410 # test_classifier.TestClassifier.test_acl_ip
411 # apply filtering only if it is so
412 if len(parts) == 3:
413 if not filter_cb(parts[0], parts[1], parts[2]):
414 continue
415 result.addTest(t)
416 else:
417 # unexpected object, don't touch it
418 result.addTest(t)
419 return result
420
421
422class FilterByTestOption:
423 def __init__(self, filter_file_name, filter_class_name, filter_func_name):
424 self.filter_file_name = filter_file_name
425 self.filter_class_name = filter_class_name
426 self.filter_func_name = filter_func_name
427
428 def __call__(self, file_name, class_name, func_name):
Andrew Yourtchenkod760f792018-10-03 11:38:31 +0200429 if self.filter_file_name:
430 fn_match = fnmatch.fnmatch(file_name, self.filter_file_name)
431 if not fn_match:
432 return False
juraj.linkes184870a2018-07-16 14:22:01 +0200433 if self.filter_class_name and class_name != self.filter_class_name:
434 return False
435 if self.filter_func_name and func_name != self.filter_func_name:
436 return False
437 return True
438
439
440class FilterByClassList:
juraj.linkes721872e2018-09-05 18:13:45 +0200441 def __init__(self, classes_with_filenames):
442 self.classes_with_filenames = classes_with_filenames
Klement Sekeradf2b9802017-10-05 10:26:03 +0200443
444 def __call__(self, file_name, class_name, func_name):
juraj.linkes721872e2018-09-05 18:13:45 +0200445 return '.'.join([file_name, class_name]) in self.classes_with_filenames
Klement Sekeradf2b9802017-10-05 10:26:03 +0200446
447
448def suite_from_failed(suite, failed):
juraj.linkes721872e2018-09-05 18:13:45 +0200449 failed = {x.rsplit('.', 1)[0] for x in failed}
juraj.linkes184870a2018-07-16 14:22:01 +0200450 filter_cb = FilterByClassList(failed)
451 suite = filter_tests(suite, filter_cb)
Klement Sekera4c5422e2018-06-22 13:19:45 +0200452 return suite
Klement Sekeradf2b9802017-10-05 10:26:03 +0200453
454
juraj.linkescae64f82018-09-19 15:01:47 +0200455class AllResults(dict):
juraj.linkes184870a2018-07-16 14:22:01 +0200456 def __init__(self):
juraj.linkescae64f82018-09-19 15:01:47 +0200457 super(AllResults, self).__init__()
juraj.linkes184870a2018-07-16 14:22:01 +0200458 self.all_testcases = 0
juraj.linkescae64f82018-09-19 15:01:47 +0200459 self.results_per_suite = []
460 self[PASS] = 0
461 self[FAIL] = 0
462 self[ERROR] = 0
463 self[SKIP] = 0
464 self[TEST_RUN] = 0
juraj.linkes184870a2018-07-16 14:22:01 +0200465 self.rerun = []
juraj.linkescae64f82018-09-19 15:01:47 +0200466 self.testsuites_no_tests_run = []
Klement Sekera909a6a12017-08-08 04:33:53 +0200467
juraj.linkescae64f82018-09-19 15:01:47 +0200468 def add_results(self, result):
469 self.results_per_suite.append(result)
470 result_types = [PASS, FAIL, ERROR, SKIP, TEST_RUN]
471 for result_type in result_types:
472 self[result_type] += len(result[result_type])
Klement Sekera05742262018-03-14 18:14:49 +0100473
juraj.linkescae64f82018-09-19 15:01:47 +0200474 def add_result(self, result):
juraj.linkes184870a2018-07-16 14:22:01 +0200475 retval = 0
juraj.linkescae64f82018-09-19 15:01:47 +0200476 self.all_testcases += result.testcase_suite.countTestCases()
477 if not result.no_tests_run():
478 if not result.was_successful():
juraj.linkes184870a2018-07-16 14:22:01 +0200479 retval = 1
480
juraj.linkescae64f82018-09-19 15:01:47 +0200481 self.add_results(result)
juraj.linkes721872e2018-09-05 18:13:45 +0200482 else:
juraj.linkescae64f82018-09-19 15:01:47 +0200483 self.testsuites_no_tests_run.append(result.testcase_suite)
juraj.linkes721872e2018-09-05 18:13:45 +0200484 retval = -1
juraj.linkes184870a2018-07-16 14:22:01 +0200485
juraj.linkes184870a2018-07-16 14:22:01 +0200486 if retval != 0:
487 if concurrent_tests == 1:
juraj.linkescae64f82018-09-19 15:01:47 +0200488 if not result.no_tests_run():
489 self.rerun.append(result.suite_from_failed())
juraj.linkes184870a2018-07-16 14:22:01 +0200490 else:
juraj.linkescae64f82018-09-19 15:01:47 +0200491 self.rerun.append(result.testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200492 else:
juraj.linkescae64f82018-09-19 15:01:47 +0200493 self.rerun.append(result.testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200494
495 return retval
496
497 def print_results(self):
498 print('')
499 print(double_line_delim)
500 print('TEST RESULTS:')
juraj.linkescae64f82018-09-19 15:01:47 +0200501 print(' Scheduled tests: {}'.format(self.all_testcases))
502 print(' Executed tests: {}'.format(self[TEST_RUN]))
503 print(' Passed tests: {}'.format(
504 colorize(str(self[PASS]), GREEN)))
505 if self[SKIP] > 0:
506 print(' Skipped tests: {}'.format(
507 colorize(str(self[SKIP]), YELLOW)))
508 if self.not_executed > 0:
509 print(' Not Executed tests: {}'.format(
510 colorize(str(self.not_executed), RED)))
511 if self[FAIL] > 0:
512 print(' Failures: {}'.format(
513 colorize(str(self[FAIL]), RED)))
514 if self[ERROR] > 0:
515 print(' Errors: {}'.format(
516 colorize(str(self[ERROR]), RED)))
juraj.linkes184870a2018-07-16 14:22:01 +0200517
518 if self.all_failed > 0:
519 print('FAILED TESTS:')
juraj.linkescae64f82018-09-19 15:01:47 +0200520 for result in self.results_per_suite:
521 failed_testcase_ids = result[FAIL]
522 errored_testcase_ids = result[ERROR]
523 old_testcase_name = None
524 if len(failed_testcase_ids) or len(errored_testcase_ids):
525 for failed_test_id in failed_testcase_ids:
526 new_testcase_name, test_name = \
527 result.get_testcase_names(failed_test_id)
528 if new_testcase_name != old_testcase_name:
529 print(' Testcase name: {}'.format(
530 colorize(new_testcase_name, RED)))
531 old_testcase_name = new_testcase_name
juraj.linkes184870a2018-07-16 14:22:01 +0200532 print(' FAILED: {}'.format(
juraj.linkescae64f82018-09-19 15:01:47 +0200533 colorize(test_name, RED)))
534 for failed_test_id in errored_testcase_ids:
535 new_testcase_name, test_name = \
536 result.get_testcase_names(failed_test_id)
537 if new_testcase_name != old_testcase_name:
538 print(' Testcase name: {}'.format(
539 colorize(new_testcase_name, RED)))
540 old_testcase_name = new_testcase_name
juraj.linkes184870a2018-07-16 14:22:01 +0200541 print(' ERRORED: {}'.format(
juraj.linkescae64f82018-09-19 15:01:47 +0200542 colorize(test_name, RED)))
543 if len(self.testsuites_no_tests_run) > 0:
544 print('TESTCASES WHERE NO TESTS WERE SUCCESSFULLY EXECUTED:')
545 tc_classes = set([])
546 for testsuite in self.testsuites_no_tests_run:
547 for testcase in testsuite:
548 tc_classes.add(get_testcase_doc_name(testcase))
549 for tc_class in tc_classes:
550 print(' {}'.format(colorize(tc_class, RED)))
juraj.linkes184870a2018-07-16 14:22:01 +0200551
552 print(double_line_delim)
553 print('')
554
555 @property
juraj.linkescae64f82018-09-19 15:01:47 +0200556 def not_executed(self):
557 return self.all_testcases - self[TEST_RUN]
558
559 @property
juraj.linkes184870a2018-07-16 14:22:01 +0200560 def all_failed(self):
juraj.linkescae64f82018-09-19 15:01:47 +0200561 return self[FAIL] + self[ERROR]
juraj.linkes184870a2018-07-16 14:22:01 +0200562
563
564def parse_results(results):
565 """
juraj.linkescae64f82018-09-19 15:01:47 +0200566 Prints the number of scheduled, executed, not executed, passed, failed,
567 errored and skipped tests and details about failed and errored tests.
juraj.linkes184870a2018-07-16 14:22:01 +0200568
juraj.linkescae64f82018-09-19 15:01:47 +0200569 Also returns all suites where any test failed.
juraj.linkes184870a2018-07-16 14:22:01 +0200570
571 :param results:
572 :return:
573 """
574
juraj.linkescae64f82018-09-19 15:01:47 +0200575 results_per_suite = AllResults()
juraj.linkes184870a2018-07-16 14:22:01 +0200576 crashed = False
577 failed = False
juraj.linkescae64f82018-09-19 15:01:47 +0200578 for result in results:
579 result_code = results_per_suite.add_result(result)
juraj.linkes184870a2018-07-16 14:22:01 +0200580 if result_code == 1:
581 failed = True
582 elif result_code == -1:
583 crashed = True
584
585 results_per_suite.print_results()
586
587 if crashed:
588 return_code = -1
589 elif failed:
590 return_code = 1
591 else:
592 return_code = 0
593 return return_code, results_per_suite.rerun
594
595
596def parse_digit_env(env_var, default):
597 value = os.getenv(env_var, default)
598 if value != default:
599 if value.isdigit():
600 value = int(value)
601 else:
602 print('WARNING: unsupported value "%s" for env var "%s",'
603 'defaulting to %s' % (value, env_var, default))
604 value = default
605 return value
Klement Sekera3f6ff192017-08-11 06:56:05 +0200606
607
608if __name__ == '__main__':
609
juraj.linkes184870a2018-07-16 14:22:01 +0200610 verbose = parse_digit_env("V", 0)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200611
juraj.linkes184870a2018-07-16 14:22:01 +0200612 test_timeout = parse_digit_env("TIMEOUT", 600) # default = 10 minutes
Klement Sekera3f6ff192017-08-11 06:56:05 +0200613
juraj.linkes184870a2018-07-16 14:22:01 +0200614 retries = parse_digit_env("RETRIES", 0)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200615
juraj.linkes184870a2018-07-16 14:22:01 +0200616 debug = os.getenv("DEBUG", "n").lower() in ["gdb", "gdbserver"]
617
618 step = os.getenv("STEP", "n").lower() in ("y", "yes", "1")
619
620 force_foreground = \
621 os.getenv("FORCE_FOREGROUND", "n").lower() in ("y", "yes", "1")
622
623 run_interactive = debug or step or force_foreground
624
625 test_jobs = os.getenv("TEST_JOBS", "1").lower() # default = 1 process
626 if test_jobs == 'auto':
627 if run_interactive:
628 concurrent_tests = 1
629 print('Interactive mode required, running on one core')
630 else:
631 shm_free = psutil.disk_usage('/dev/shm').free
632 shm_max_processes = 1
633 if shm_free < min_req_shm:
634 raise Exception('Not enough free space in /dev/shm. Required '
635 'free space is at least %sM.'
636 % (min_req_shm >> 20))
637 else:
638 extra_shm = shm_free - min_req_shm
639 shm_max_processes += extra_shm / shm_per_process
Klement Sekera6c7bda92018-09-18 14:32:27 +0200640 concurrent_tests = min(cpu_count(), shm_max_processes)
juraj.linkes184870a2018-07-16 14:22:01 +0200641 print('Found enough resources to run tests with %s cores'
642 % concurrent_tests)
643 elif test_jobs.isdigit():
644 concurrent_tests = int(test_jobs)
645 else:
646 concurrent_tests = 1
647
648 if run_interactive and concurrent_tests > 1:
649 raise NotImplementedError(
650 'Running tests interactively (DEBUG, STEP or FORCE_FOREGROUND is '
651 'set) in parallel (TEST_JOBS is more than 1) is not '
652 'supported')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100653
Klement Sekera3f6ff192017-08-11 06:56:05 +0200654 parser = argparse.ArgumentParser(description="VPP unit tests")
juraj.linkes184870a2018-07-16 14:22:01 +0200655 parser.add_argument("-f", "--failfast", action='store_true',
Klement Sekera3f6ff192017-08-11 06:56:05 +0200656 help="fast failure flag")
657 parser.add_argument("-d", "--dir", action='append', type=str,
658 help="directory containing test files "
659 "(may be specified multiple times)")
660 args = parser.parse_args()
juraj.linkes184870a2018-07-16 14:22:01 +0200661 failfast = args.failfast
662 descriptions = True
Klement Sekera3f6ff192017-08-11 06:56:05 +0200663
juraj.linkes184870a2018-07-16 14:22:01 +0200664 print("Running tests using custom test runner") # debug message
665 filter_file, filter_class, filter_func = parse_test_option()
666
667 print("Active filters: file=%s, class=%s, function=%s" % (
668 filter_file, filter_class, filter_func))
669
670 filter_cb = FilterByTestOption(filter_file, filter_class, filter_func)
671
672 cb = SplitToSuitesCallback(filter_cb)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200673 for d in args.dir:
Klement Sekeradf2b9802017-10-05 10:26:03 +0200674 print("Adding tests from directory tree %s" % d)
Klement Sekerafcbf4442017-08-17 07:38:42 +0200675 discover_tests(d, cb)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200676
juraj.linkes184870a2018-07-16 14:22:01 +0200677 # suites are not hashable, need to use list
678 suites = []
679 tests_amount = 0
680 for testcase_suite in cb.suites.values():
681 tests_amount += testcase_suite.countTestCases()
682 suites.append(testcase_suite)
Klement Sekerabbfa5fd2018-06-27 13:54:32 +0200683
juraj.linkes184870a2018-07-16 14:22:01 +0200684 if concurrent_tests == 1:
685 new_suite = unittest.TestSuite()
686 for suite in suites:
juraj.linkes721872e2018-09-05 18:13:45 +0200687 new_suite.addTests(suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200688
689 suites = [new_suite]
690
691 print("%s out of %s tests match specified filters" % (
692 tests_amount, tests_amount + cb.filtered.countTestCases()))
693
694 if not running_extended_tests():
695 print("Not running extended tests (some tests will be skipped)")
696
Klement Sekeradf2b9802017-10-05 10:26:03 +0200697 attempts = retries + 1
698 if attempts > 1:
699 print("Perform %s attempts to pass the suite..." % attempts)
juraj.linkes184870a2018-07-16 14:22:01 +0200700
701 if run_interactive:
702 # don't fork if requiring interactive terminal
Klement Sekera13a83ef2018-03-21 12:35:51 +0100703 sys.exit(not VppTestRunner(
juraj.linkes184870a2018-07-16 14:22:01 +0200704 verbosity=verbose, failfast=failfast)
705 .run(suites[0]).wasSuccessful())
Klement Sekera13a83ef2018-03-21 12:35:51 +0100706 else:
juraj.linkes184870a2018-07-16 14:22:01 +0200707 exit_code = 0
708 while len(suites) > 0 and attempts > 0:
709 tests_amount = sum([x.countTestCases() for x in suites])
710 results = run_forked(suites)
711 exit_code, suites = parse_results(results)
712 attempts -= 1
713 if exit_code == 0:
714 print('Test run was successful')
715 else:
716 print('%s attempt(s) left.' % attempts)
717 sys.exit(exit_code)