blob: f5d5211078e2c2832907c783d1376bd0b6c46376 [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
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
juraj.linkes40dd73b2018-09-21 13:55:16 +020013import re
juraj.linkes184870a2018-07-16 14:22:01 +020014from multiprocessing import Process, Pipe, cpu_count
15from multiprocessing.queues import Queue
16from multiprocessing.managers import BaseManager
17from framework import VppTestRunner, running_extended_tests, VppTestCase, \
Ole Trøan5ba91592018-11-22 10:01:09 +000018 get_testcase_doc_name, get_test_description, PASS, FAIL, ERROR, SKIP, \
juraj.linkescae64f82018-09-19 15:01:47 +020019 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, \
juraj.linkes40dd73b2018-09-21 13:55:16 +020022 colorize, single_line_delim
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
juraj.linkes40dd73b2018-09-21 13:55:16 +020025from util import check_core_path, get_core_path, is_core_present
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):
juraj.linkes40dd73b2018-09-21 13:55:16 +020057 def __init__(self, testcase_suite, testcases_by_id=None):
juraj.linkescae64f82018-09-19 15:01:47 +020058 super(TestResult, self).__init__()
59 self[PASS] = []
60 self[FAIL] = []
61 self[ERROR] = []
62 self[SKIP] = []
63 self[TEST_RUN] = []
juraj.linkes40dd73b2018-09-21 13:55:16 +020064 self.crashed = False
juraj.linkescae64f82018-09-19 15:01:47 +020065 self.testcase_suite = testcase_suite
66 self.testcases = [testcase for testcase in testcase_suite]
juraj.linkes40dd73b2018-09-21 13:55:16 +020067 self.testcases_by_id = testcases_by_id
juraj.linkescae64f82018-09-19 15:01:47 +020068
69 def was_successful(self):
juraj.linkes40dd73b2018-09-21 13:55:16 +020070 return 0 == len(self[FAIL]) == len(self[ERROR]) \
Klement Sekerab8c72a42018-11-08 11:21:39 +010071 and len(self[PASS] + self[SKIP]) \
72 == self.testcase_suite.countTestCases() == len(self[TEST_RUN])
juraj.linkescae64f82018-09-19 15:01:47 +020073
74 def no_tests_run(self):
75 return 0 == len(self[TEST_RUN])
76
77 def process_result(self, test_id, result):
78 self[result].append(test_id)
juraj.linkescae64f82018-09-19 15:01:47 +020079
80 def suite_from_failed(self):
81 rerun_ids = set([])
82 for testcase in self.testcase_suite:
83 tc_id = testcase.id()
84 if tc_id not in self[PASS] and tc_id not in self[SKIP]:
85 rerun_ids.add(tc_id)
86 if len(rerun_ids) > 0:
87 return suite_from_failed(self.testcase_suite, rerun_ids)
88
89 def get_testcase_names(self, test_id):
juraj.linkes40dd73b2018-09-21 13:55:16 +020090 if re.match(r'.+\..+\..+', test_id):
Ole Trøan5ba91592018-11-22 10:01:09 +000091 test_name = self._get_test_description(test_id)
juraj.linkes40dd73b2018-09-21 13:55:16 +020092 testcase_name = self._get_testcase_doc_name(test_id)
93 else:
94 # could be tearDownClass (test_ipsec_esp.TestIpsecEsp1)
95 setup_teardown_match = re.match(
96 r'((tearDownClass)|(setUpClass)) \((.+\..+)\)', test_id)
97 if setup_teardown_match:
98 test_name, _, _, testcase_name = setup_teardown_match.groups()
99 if len(testcase_name.split('.')) == 2:
100 for key in self.testcases_by_id.keys():
101 if key.startswith(testcase_name):
102 testcase_name = key
103 break
104 testcase_name = self._get_testcase_doc_name(testcase_name)
105 else:
106 test_name = test_id
107 testcase_name = test_id
108
109 return testcase_name, test_name
juraj.linkescae64f82018-09-19 15:01:47 +0200110
Ole Trøan5ba91592018-11-22 10:01:09 +0000111 def _get_test_description(self, test_id):
112 return get_test_description(descriptions,
113 self.testcases_by_id[test_id])
114
juraj.linkes40dd73b2018-09-21 13:55:16 +0200115 def _get_testcase_doc_name(self, test_id):
116 return get_testcase_doc_name(self.testcases_by_id[test_id])
juraj.linkescae64f82018-09-19 15:01:47 +0200117
118
119def test_runner_wrapper(suite, keep_alive_pipe, stdouterr_queue,
120 finished_pipe, result_pipe, logger):
juraj.linkes184870a2018-07-16 14:22:01 +0200121 sys.stdout = stdouterr_queue
122 sys.stderr = stdouterr_queue
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100123 VppTestCase.parallel_handler = logger.handlers[0]
juraj.linkes184870a2018-07-16 14:22:01 +0200124 result = VppTestRunner(keep_alive_pipe=keep_alive_pipe,
125 descriptions=descriptions,
126 verbosity=verbose,
juraj.linkescae64f82018-09-19 15:01:47 +0200127 result_pipe=result_pipe,
juraj.linkesabec0122018-11-16 17:28:56 +0100128 failfast=failfast,
129 print_summary=False).run(suite)
juraj.linkescae64f82018-09-19 15:01:47 +0200130 finished_pipe.send(result.wasSuccessful())
131 finished_pipe.close()
Klement Sekera909a6a12017-08-08 04:33:53 +0200132 keep_alive_pipe.close()
133
134
juraj.linkes184870a2018-07-16 14:22:01 +0200135class TestCaseWrapper(object):
136 def __init__(self, testcase_suite, manager):
137 self.keep_alive_parent_end, self.keep_alive_child_end = Pipe(
138 duplex=False)
juraj.linkescae64f82018-09-19 15:01:47 +0200139 self.finished_parent_end, self.finished_child_end = Pipe(duplex=False)
juraj.linkes184870a2018-07-16 14:22:01 +0200140 self.result_parent_end, self.result_child_end = Pipe(duplex=False)
141 self.testcase_suite = testcase_suite
Ole Troan7f991832018-12-06 17:35:12 +0100142 if sys.version[0] == '2':
143 self.stdouterr_queue = manager.StreamQueue()
144 else:
145 from multiprocessing import get_context
146 self.stdouterr_queue = manager.StreamQueue(ctx=get_context())
juraj.linkes184870a2018-07-16 14:22:01 +0200147 self.logger = get_parallel_logger(self.stdouterr_queue)
148 self.child = Process(target=test_runner_wrapper,
juraj.linkescae64f82018-09-19 15:01:47 +0200149 args=(testcase_suite,
150 self.keep_alive_child_end,
151 self.stdouterr_queue,
152 self.finished_child_end,
153 self.result_child_end,
154 self.logger)
juraj.linkes184870a2018-07-16 14:22:01 +0200155 )
156 self.child.start()
juraj.linkes184870a2018-07-16 14:22:01 +0200157 self.last_test_temp_dir = None
158 self.last_test_vpp_binary = None
juraj.linkes40dd73b2018-09-21 13:55:16 +0200159 self._last_test = None
160 self.last_test_id = None
juraj.linkes721872e2018-09-05 18:13:45 +0200161 self.vpp_pid = None
juraj.linkes184870a2018-07-16 14:22:01 +0200162 self.last_heard = time.time()
163 self.core_detected_at = None
juraj.linkes40dd73b2018-09-21 13:55:16 +0200164 self.testcases_by_id = {}
165 self.testclasess_with_core = {}
166 for testcase in self.testcase_suite:
167 self.testcases_by_id[testcase.id()] = testcase
168 self.result = TestResult(testcase_suite, self.testcases_by_id)
169
170 @property
171 def last_test(self):
172 return self._last_test
173
174 @last_test.setter
175 def last_test(self, test_id):
176 self.last_test_id = test_id
177 if test_id in self.testcases_by_id:
178 testcase = self.testcases_by_id[test_id]
179 self._last_test = testcase.shortDescription()
180 if not self._last_test:
181 self._last_test = str(testcase)
182 else:
183 self._last_test = test_id
184
185 def add_testclass_with_core(self):
186 if self.last_test_id in self.testcases_by_id:
187 test = self.testcases_by_id[self.last_test_id]
188 class_name = unittest.util.strclass(test.__class__)
189 test_name = "'{}' ({})".format(get_test_description(descriptions,
190 test),
191 self.last_test_id)
192 else:
193 test_name = self.last_test_id
194 class_name = re.match(r'((tearDownClass)|(setUpClass)) '
195 r'\((.+\..+)\)', test_name).groups()[3]
196 if class_name not in self.testclasess_with_core:
197 self.testclasess_with_core[class_name] = (
198 test_name,
199 self.last_test_vpp_binary,
200 self.last_test_temp_dir)
juraj.linkes184870a2018-07-16 14:22:01 +0200201
202 def close_pipes(self):
203 self.keep_alive_child_end.close()
juraj.linkescae64f82018-09-19 15:01:47 +0200204 self.finished_child_end.close()
juraj.linkes184870a2018-07-16 14:22:01 +0200205 self.result_child_end.close()
206 self.keep_alive_parent_end.close()
juraj.linkescae64f82018-09-19 15:01:47 +0200207 self.finished_parent_end.close()
juraj.linkes184870a2018-07-16 14:22:01 +0200208 self.result_parent_end.close()
209
juraj.linkes40dd73b2018-09-21 13:55:16 +0200210 def was_successful(self):
211 return self.result.was_successful()
212
juraj.linkes184870a2018-07-16 14:22:01 +0200213
214def stdouterr_reader_wrapper(unread_testcases, finished_unread_testcases,
215 read_testcases):
216 read_testcase = None
juraj.linkese6b58cf2018-11-29 09:56:35 +0100217 while read_testcases.is_set() or len(unread_testcases):
218 if len(finished_unread_testcases):
219 read_testcase = finished_unread_testcases.pop()
220 unread_testcases.remove(read_testcase)
221 elif len(unread_testcases):
222 read_testcase = unread_testcases.pop()
juraj.linkes184870a2018-07-16 14:22:01 +0200223 if read_testcase:
224 data = ''
225 while data is not None:
226 sys.stdout.write(data)
227 data = read_testcase.stdouterr_queue.get()
228
229 read_testcase.stdouterr_queue.close()
230 finished_unread_testcases.discard(read_testcase)
231 read_testcase = None
232
233
juraj.linkes40dd73b2018-09-21 13:55:16 +0200234def handle_failed_suite(logger, last_test_temp_dir, vpp_pid):
235 if last_test_temp_dir:
236 # Need to create link in case of a timeout or core dump without failure
237 lttd = os.path.basename(last_test_temp_dir)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100238 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +0200239 link_path = '%s%s-FAILED' % (failed_dir, lttd)
240 if not os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200241 os.symlink(last_test_temp_dir, link_path)
juraj.linkesabec0122018-11-16 17:28:56 +0100242 logger.error("Symlink to failed testcase directory: %s -> %s"
243 % (link_path, lttd))
juraj.linkes40dd73b2018-09-21 13:55:16 +0200244
245 # Report core existence
246 core_path = get_core_path(last_test_temp_dir)
247 if os.path.exists(core_path):
248 logger.error(
249 "Core-file exists in test temporary directory: %s!" %
250 core_path)
251 check_core_path(logger, core_path)
252 logger.debug("Running `file %s':" % core_path)
253 try:
254 info = check_output(["file", core_path])
255 logger.debug(info)
256 except CalledProcessError as e:
257 logger.error("Could not run `file' utility on core-file, "
258 "rc=%s" % e.returncode)
259
260 if vpp_pid:
261 # Copy api post mortem
262 api_post_mortem_path = "/tmp/api_post_mortem.%d" % vpp_pid
263 if os.path.isfile(api_post_mortem_path):
264 logger.error("Copying api_post_mortem.%d to %s" %
265 (vpp_pid, last_test_temp_dir))
266 shutil.copy2(api_post_mortem_path, last_test_temp_dir)
267
268
269def check_and_handle_core(vpp_binary, tempdir, core_crash_test):
270 if is_core_present(tempdir):
271 print('VPP core detected in %s. Last test running was %s' %
272 (tempdir, core_crash_test))
273 print(single_line_delim)
274 spawn_gdb(vpp_binary, get_core_path(tempdir))
275 print(single_line_delim)
276
277
278def handle_cores(failed_testcases):
279 if debug_core:
280 for failed_testcase in failed_testcases:
281 tcs_with_core = failed_testcase.testclasess_with_core
282 if len(tcs_with_core) > 0:
283 for test, vpp_binary, tempdir in tcs_with_core.values():
284 check_and_handle_core(vpp_binary, tempdir, test)
285
286
287def process_finished_testsuite(wrapped_testcase_suite,
288 finished_testcase_suites,
289 failed_wrapped_testcases,
290 results):
291 results.append(wrapped_testcase_suite.result)
292 finished_testcase_suites.add(wrapped_testcase_suite)
293 stop_run = False
294 if failfast and not wrapped_testcase_suite.was_successful():
295 stop_run = True
296
297 if not wrapped_testcase_suite.was_successful():
298 failed_wrapped_testcases.add(wrapped_testcase_suite)
299 handle_failed_suite(wrapped_testcase_suite.logger,
300 wrapped_testcase_suite.last_test_temp_dir,
301 wrapped_testcase_suite.vpp_pid)
302
303 return stop_run
304
305
juraj.linkes721872e2018-09-05 18:13:45 +0200306def run_forked(testcase_suites):
juraj.linkes184870a2018-07-16 14:22:01 +0200307 wrapped_testcase_suites = set()
308
309 # suites are unhashable, need to use list
310 results = []
juraj.linkes184870a2018-07-16 14:22:01 +0200311 unread_testcases = set()
312 finished_unread_testcases = set()
313 manager = StreamQueueManager()
314 manager.start()
315 for i in range(concurrent_tests):
juraj.linkes721872e2018-09-05 18:13:45 +0200316 if len(testcase_suites) > 0:
317 wrapped_testcase_suite = TestCaseWrapper(testcase_suites.pop(0),
318 manager)
juraj.linkes184870a2018-07-16 14:22:01 +0200319 wrapped_testcase_suites.add(wrapped_testcase_suite)
320 unread_testcases.add(wrapped_testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200321 else:
322 break
323
324 read_from_testcases = threading.Event()
325 read_from_testcases.set()
326 stdouterr_thread = threading.Thread(target=stdouterr_reader_wrapper,
327 args=(unread_testcases,
328 finished_unread_testcases,
329 read_from_testcases))
330 stdouterr_thread.start()
331
juraj.linkes40dd73b2018-09-21 13:55:16 +0200332 failed_wrapped_testcases = set()
333 stop_run = False
juraj.linkese6b58cf2018-11-29 09:56:35 +0100334
335 try:
336 while len(wrapped_testcase_suites) > 0:
337 finished_testcase_suites = set()
338 for wrapped_testcase_suite in wrapped_testcase_suites:
339 while wrapped_testcase_suite.result_parent_end.poll():
340 wrapped_testcase_suite.result.process_result(
341 *wrapped_testcase_suite.result_parent_end.recv())
342 wrapped_testcase_suite.last_heard = time.time()
343
344 while wrapped_testcase_suite.keep_alive_parent_end.poll():
345 wrapped_testcase_suite.last_test, \
346 wrapped_testcase_suite.last_test_vpp_binary, \
347 wrapped_testcase_suite.last_test_temp_dir, \
348 wrapped_testcase_suite.vpp_pid = \
349 wrapped_testcase_suite.keep_alive_parent_end.recv()
350 wrapped_testcase_suite.last_heard = time.time()
351
352 if wrapped_testcase_suite.finished_parent_end.poll():
353 wrapped_testcase_suite.finished_parent_end.recv()
354 wrapped_testcase_suite.last_heard = time.time()
355 stop_run = process_finished_testsuite(
356 wrapped_testcase_suite,
357 finished_testcase_suites,
358 failed_wrapped_testcases,
359 results) or stop_run
360 continue
361
362 fail = False
363 if wrapped_testcase_suite.last_heard + test_timeout < \
364 time.time():
365 fail = True
366 wrapped_testcase_suite.logger.critical(
367 "Child test runner process timed out "
368 "(last test running was `%s' in `%s')!" %
369 (wrapped_testcase_suite.last_test,
370 wrapped_testcase_suite.last_test_temp_dir))
371 elif not wrapped_testcase_suite.child.is_alive():
372 fail = True
373 wrapped_testcase_suite.logger.critical(
374 "Child test runner process unexpectedly died "
375 "(last test running was `%s' in `%s')!" %
376 (wrapped_testcase_suite.last_test,
377 wrapped_testcase_suite.last_test_temp_dir))
378 elif wrapped_testcase_suite.last_test_temp_dir and \
379 wrapped_testcase_suite.last_test_vpp_binary:
380 if is_core_present(
381 wrapped_testcase_suite.last_test_temp_dir):
382 wrapped_testcase_suite.add_testclass_with_core()
383 if wrapped_testcase_suite.core_detected_at is None:
384 wrapped_testcase_suite.core_detected_at = \
385 time.time()
386 elif wrapped_testcase_suite.core_detected_at + \
387 core_timeout < time.time():
388 wrapped_testcase_suite.logger.critical(
389 "Child test runner process unresponsive and "
390 "core-file exists in test temporary directory "
391 "(last test running was `%s' in `%s')!" %
392 (wrapped_testcase_suite.last_test,
393 wrapped_testcase_suite.last_test_temp_dir))
394 fail = True
395
396 if fail:
397 wrapped_testcase_suite.child.terminate()
398 try:
399 # terminating the child process tends to leave orphan
400 # VPP process around
401 if wrapped_testcase_suite.vpp_pid:
402 os.kill(wrapped_testcase_suite.vpp_pid,
403 signal.SIGTERM)
404 except OSError:
405 # already dead
406 pass
407 wrapped_testcase_suite.result.crashed = True
408 wrapped_testcase_suite.result.process_result(
409 wrapped_testcase_suite.last_test_id, ERROR)
410 stop_run = process_finished_testsuite(
411 wrapped_testcase_suite,
412 finished_testcase_suites,
413 failed_wrapped_testcases,
414 results) or stop_run
415
416 for finished_testcase in finished_testcase_suites:
417 finished_testcase.child.join()
418 finished_testcase.close_pipes()
419 wrapped_testcase_suites.remove(finished_testcase)
420 finished_unread_testcases.add(finished_testcase)
421 finished_testcase.stdouterr_queue.put(None)
422 if stop_run:
423 while len(testcase_suites) > 0:
424 results.append(TestResult(testcase_suites.pop(0)))
425 elif len(testcase_suites) > 0:
426 new_testcase = TestCaseWrapper(testcase_suites.pop(0),
427 manager)
428 wrapped_testcase_suites.add(new_testcase)
429 unread_testcases.add(new_testcase)
430 except Exception:
juraj.linkes184870a2018-07-16 14:22:01 +0200431 for wrapped_testcase_suite in wrapped_testcase_suites:
juraj.linkese6b58cf2018-11-29 09:56:35 +0100432 wrapped_testcase_suite.child.terminate()
433 wrapped_testcase_suite.stdouterr_queue.put(None)
434 raise
435 finally:
436 read_from_testcases.clear()
437 stdouterr_thread.join(test_timeout)
438 manager.shutdown()
juraj.linkescae64f82018-09-19 15:01:47 +0200439
juraj.linkes40dd73b2018-09-21 13:55:16 +0200440 handle_cores(failed_wrapped_testcases)
juraj.linkes184870a2018-07-16 14:22:01 +0200441 return results
442
443
444class SplitToSuitesCallback:
445 def __init__(self, filter_callback):
446 self.suites = {}
447 self.suite_name = 'default'
448 self.filter_callback = filter_callback
449 self.filtered = unittest.TestSuite()
Klement Sekerafcbf4442017-08-17 07:38:42 +0200450
451 def __call__(self, file_name, cls, method):
juraj.linkes184870a2018-07-16 14:22:01 +0200452 test_method = cls(method)
453 if self.filter_callback(file_name, cls.__name__, method):
454 self.suite_name = file_name + cls.__name__
455 if self.suite_name not in self.suites:
456 self.suites[self.suite_name] = unittest.TestSuite()
457 self.suites[self.suite_name].addTest(test_method)
458
459 else:
460 self.filtered.addTest(test_method)
Klement Sekerafcbf4442017-08-17 07:38:42 +0200461
462
juraj.linkes184870a2018-07-16 14:22:01 +0200463test_option = "TEST"
464
465
466def parse_test_option():
467 f = os.getenv(test_option, None)
468 filter_file_name = None
469 filter_class_name = None
470 filter_func_name = None
471 if f:
472 if '.' in f:
473 parts = f.split('.')
474 if len(parts) > 3:
475 raise Exception("Unrecognized %s option: %s" %
476 (test_option, f))
477 if len(parts) > 2:
478 if parts[2] not in ('*', ''):
479 filter_func_name = parts[2]
480 if parts[1] not in ('*', ''):
481 filter_class_name = parts[1]
482 if parts[0] not in ('*', ''):
483 if parts[0].startswith('test_'):
484 filter_file_name = parts[0]
485 else:
486 filter_file_name = 'test_%s' % parts[0]
487 else:
488 if f.startswith('test_'):
489 filter_file_name = f
490 else:
491 filter_file_name = 'test_%s' % f
492 if filter_file_name:
493 filter_file_name = '%s.py' % filter_file_name
494 return filter_file_name, filter_class_name, filter_func_name
495
496
497def filter_tests(tests, filter_cb):
498 result = unittest.suite.TestSuite()
499 for t in tests:
500 if isinstance(t, unittest.suite.TestSuite):
501 # this is a bunch of tests, recursively filter...
502 x = filter_tests(t, filter_cb)
503 if x.countTestCases() > 0:
504 result.addTest(x)
505 elif isinstance(t, unittest.TestCase):
506 # this is a single test
507 parts = t.id().split('.')
508 # t.id() for common cases like this:
509 # test_classifier.TestClassifier.test_acl_ip
510 # apply filtering only if it is so
511 if len(parts) == 3:
512 if not filter_cb(parts[0], parts[1], parts[2]):
513 continue
514 result.addTest(t)
515 else:
516 # unexpected object, don't touch it
517 result.addTest(t)
518 return result
519
520
521class FilterByTestOption:
522 def __init__(self, filter_file_name, filter_class_name, filter_func_name):
523 self.filter_file_name = filter_file_name
524 self.filter_class_name = filter_class_name
525 self.filter_func_name = filter_func_name
526
527 def __call__(self, file_name, class_name, func_name):
Andrew Yourtchenkod760f792018-10-03 11:38:31 +0200528 if self.filter_file_name:
529 fn_match = fnmatch.fnmatch(file_name, self.filter_file_name)
530 if not fn_match:
531 return False
juraj.linkes184870a2018-07-16 14:22:01 +0200532 if self.filter_class_name and class_name != self.filter_class_name:
533 return False
534 if self.filter_func_name and func_name != self.filter_func_name:
535 return False
536 return True
537
538
539class FilterByClassList:
juraj.linkes721872e2018-09-05 18:13:45 +0200540 def __init__(self, classes_with_filenames):
541 self.classes_with_filenames = classes_with_filenames
Klement Sekeradf2b9802017-10-05 10:26:03 +0200542
543 def __call__(self, file_name, class_name, func_name):
juraj.linkes721872e2018-09-05 18:13:45 +0200544 return '.'.join([file_name, class_name]) in self.classes_with_filenames
Klement Sekeradf2b9802017-10-05 10:26:03 +0200545
546
547def suite_from_failed(suite, failed):
juraj.linkes721872e2018-09-05 18:13:45 +0200548 failed = {x.rsplit('.', 1)[0] for x in failed}
juraj.linkes184870a2018-07-16 14:22:01 +0200549 filter_cb = FilterByClassList(failed)
550 suite = filter_tests(suite, filter_cb)
Klement Sekera4c5422e2018-06-22 13:19:45 +0200551 return suite
Klement Sekeradf2b9802017-10-05 10:26:03 +0200552
553
juraj.linkescae64f82018-09-19 15:01:47 +0200554class AllResults(dict):
juraj.linkes184870a2018-07-16 14:22:01 +0200555 def __init__(self):
juraj.linkescae64f82018-09-19 15:01:47 +0200556 super(AllResults, self).__init__()
juraj.linkes184870a2018-07-16 14:22:01 +0200557 self.all_testcases = 0
juraj.linkescae64f82018-09-19 15:01:47 +0200558 self.results_per_suite = []
559 self[PASS] = 0
560 self[FAIL] = 0
561 self[ERROR] = 0
562 self[SKIP] = 0
563 self[TEST_RUN] = 0
juraj.linkes184870a2018-07-16 14:22:01 +0200564 self.rerun = []
juraj.linkescae64f82018-09-19 15:01:47 +0200565 self.testsuites_no_tests_run = []
Klement Sekera909a6a12017-08-08 04:33:53 +0200566
juraj.linkescae64f82018-09-19 15:01:47 +0200567 def add_results(self, result):
568 self.results_per_suite.append(result)
569 result_types = [PASS, FAIL, ERROR, SKIP, TEST_RUN]
570 for result_type in result_types:
571 self[result_type] += len(result[result_type])
Klement Sekera05742262018-03-14 18:14:49 +0100572
juraj.linkescae64f82018-09-19 15:01:47 +0200573 def add_result(self, result):
juraj.linkes184870a2018-07-16 14:22:01 +0200574 retval = 0
juraj.linkescae64f82018-09-19 15:01:47 +0200575 self.all_testcases += result.testcase_suite.countTestCases()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200576 self.add_results(result)
juraj.linkes184870a2018-07-16 14:22:01 +0200577
juraj.linkes40dd73b2018-09-21 13:55:16 +0200578 if result.no_tests_run():
juraj.linkescae64f82018-09-19 15:01:47 +0200579 self.testsuites_no_tests_run.append(result.testcase_suite)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200580 if result.crashed:
581 retval = -1
582 else:
583 retval = 1
584 elif not result.was_successful():
585 retval = 1
juraj.linkes184870a2018-07-16 14:22:01 +0200586
juraj.linkes184870a2018-07-16 14:22:01 +0200587 if retval != 0:
juraj.linkesabec0122018-11-16 17:28:56 +0100588 self.rerun.append(result.testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200589
590 return retval
591
592 def print_results(self):
593 print('')
594 print(double_line_delim)
595 print('TEST RESULTS:')
juraj.linkescae64f82018-09-19 15:01:47 +0200596 print(' Scheduled tests: {}'.format(self.all_testcases))
597 print(' Executed tests: {}'.format(self[TEST_RUN]))
598 print(' Passed tests: {}'.format(
599 colorize(str(self[PASS]), GREEN)))
600 if self[SKIP] > 0:
601 print(' Skipped tests: {}'.format(
602 colorize(str(self[SKIP]), YELLOW)))
603 if self.not_executed > 0:
604 print(' Not Executed tests: {}'.format(
605 colorize(str(self.not_executed), RED)))
606 if self[FAIL] > 0:
607 print(' Failures: {}'.format(
608 colorize(str(self[FAIL]), RED)))
609 if self[ERROR] > 0:
610 print(' Errors: {}'.format(
611 colorize(str(self[ERROR]), RED)))
juraj.linkes184870a2018-07-16 14:22:01 +0200612
613 if self.all_failed > 0:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200614 print('FAILURES AND ERRORS IN TESTS:')
juraj.linkescae64f82018-09-19 15:01:47 +0200615 for result in self.results_per_suite:
616 failed_testcase_ids = result[FAIL]
617 errored_testcase_ids = result[ERROR]
618 old_testcase_name = None
619 if len(failed_testcase_ids) or len(errored_testcase_ids):
620 for failed_test_id in failed_testcase_ids:
621 new_testcase_name, test_name = \
622 result.get_testcase_names(failed_test_id)
623 if new_testcase_name != old_testcase_name:
624 print(' Testcase name: {}'.format(
625 colorize(new_testcase_name, RED)))
626 old_testcase_name = new_testcase_name
Klement Sekera33177d62018-11-30 14:17:20 +0100627 print(' FAILURE: {} [{}]'.format(
628 colorize(test_name, RED), failed_test_id))
juraj.linkescae64f82018-09-19 15:01:47 +0200629 for failed_test_id in errored_testcase_ids:
630 new_testcase_name, test_name = \
631 result.get_testcase_names(failed_test_id)
632 if new_testcase_name != old_testcase_name:
633 print(' Testcase name: {}'.format(
634 colorize(new_testcase_name, RED)))
635 old_testcase_name = new_testcase_name
Klement Sekera33177d62018-11-30 14:17:20 +0100636 print(' ERROR: {} [{}]'.format(
637 colorize(test_name, RED), failed_test_id))
juraj.linkescae64f82018-09-19 15:01:47 +0200638 if len(self.testsuites_no_tests_run) > 0:
639 print('TESTCASES WHERE NO TESTS WERE SUCCESSFULLY EXECUTED:')
juraj.linkes40dd73b2018-09-21 13:55:16 +0200640 tc_classes = set()
juraj.linkescae64f82018-09-19 15:01:47 +0200641 for testsuite in self.testsuites_no_tests_run:
642 for testcase in testsuite:
643 tc_classes.add(get_testcase_doc_name(testcase))
644 for tc_class in tc_classes:
645 print(' {}'.format(colorize(tc_class, RED)))
juraj.linkes184870a2018-07-16 14:22:01 +0200646
647 print(double_line_delim)
648 print('')
649
650 @property
juraj.linkescae64f82018-09-19 15:01:47 +0200651 def not_executed(self):
652 return self.all_testcases - self[TEST_RUN]
653
654 @property
juraj.linkes184870a2018-07-16 14:22:01 +0200655 def all_failed(self):
juraj.linkescae64f82018-09-19 15:01:47 +0200656 return self[FAIL] + self[ERROR]
juraj.linkes184870a2018-07-16 14:22:01 +0200657
658
659def parse_results(results):
660 """
juraj.linkescae64f82018-09-19 15:01:47 +0200661 Prints the number of scheduled, executed, not executed, passed, failed,
662 errored and skipped tests and details about failed and errored tests.
juraj.linkes184870a2018-07-16 14:22:01 +0200663
juraj.linkescae64f82018-09-19 15:01:47 +0200664 Also returns all suites where any test failed.
juraj.linkes184870a2018-07-16 14:22:01 +0200665
666 :param results:
667 :return:
668 """
669
juraj.linkescae64f82018-09-19 15:01:47 +0200670 results_per_suite = AllResults()
juraj.linkes184870a2018-07-16 14:22:01 +0200671 crashed = False
672 failed = False
juraj.linkescae64f82018-09-19 15:01:47 +0200673 for result in results:
674 result_code = results_per_suite.add_result(result)
juraj.linkes184870a2018-07-16 14:22:01 +0200675 if result_code == 1:
676 failed = True
677 elif result_code == -1:
678 crashed = True
679
680 results_per_suite.print_results()
681
682 if crashed:
683 return_code = -1
684 elif failed:
685 return_code = 1
686 else:
687 return_code = 0
688 return return_code, results_per_suite.rerun
689
690
691def parse_digit_env(env_var, default):
692 value = os.getenv(env_var, default)
693 if value != default:
694 if value.isdigit():
695 value = int(value)
696 else:
697 print('WARNING: unsupported value "%s" for env var "%s",'
698 'defaulting to %s' % (value, env_var, default))
699 value = default
700 return value
Klement Sekera3f6ff192017-08-11 06:56:05 +0200701
702
703if __name__ == '__main__':
704
juraj.linkes184870a2018-07-16 14:22:01 +0200705 verbose = parse_digit_env("V", 0)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200706
juraj.linkes184870a2018-07-16 14:22:01 +0200707 test_timeout = parse_digit_env("TIMEOUT", 600) # default = 10 minutes
Klement Sekera3f6ff192017-08-11 06:56:05 +0200708
juraj.linkes184870a2018-07-16 14:22:01 +0200709 retries = parse_digit_env("RETRIES", 0)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200710
juraj.linkes184870a2018-07-16 14:22:01 +0200711 debug = os.getenv("DEBUG", "n").lower() in ["gdb", "gdbserver"]
712
juraj.linkes40dd73b2018-09-21 13:55:16 +0200713 debug_core = os.getenv("DEBUG", "").lower() == "core"
714
juraj.linkes184870a2018-07-16 14:22:01 +0200715 step = os.getenv("STEP", "n").lower() in ("y", "yes", "1")
716
juraj.linkes40dd73b2018-09-21 13:55:16 +0200717 run_interactive = debug or step
juraj.linkes184870a2018-07-16 14:22:01 +0200718
719 test_jobs = os.getenv("TEST_JOBS", "1").lower() # default = 1 process
720 if test_jobs == 'auto':
721 if run_interactive:
722 concurrent_tests = 1
723 print('Interactive mode required, running on one core')
724 else:
725 shm_free = psutil.disk_usage('/dev/shm').free
726 shm_max_processes = 1
727 if shm_free < min_req_shm:
728 raise Exception('Not enough free space in /dev/shm. Required '
729 'free space is at least %sM.'
730 % (min_req_shm >> 20))
731 else:
732 extra_shm = shm_free - min_req_shm
733 shm_max_processes += extra_shm / shm_per_process
Klement Sekera6c7bda92018-09-18 14:32:27 +0200734 concurrent_tests = min(cpu_count(), shm_max_processes)
juraj.linkes184870a2018-07-16 14:22:01 +0200735 print('Found enough resources to run tests with %s cores'
736 % concurrent_tests)
737 elif test_jobs.isdigit():
738 concurrent_tests = int(test_jobs)
739 else:
740 concurrent_tests = 1
741
742 if run_interactive and concurrent_tests > 1:
743 raise NotImplementedError(
juraj.linkes40dd73b2018-09-21 13:55:16 +0200744 'Running tests interactively (DEBUG is gdb or gdbserver or STEP '
745 'is set) in parallel (TEST_JOBS is more than 1) is not supported')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100746
Klement Sekera3f6ff192017-08-11 06:56:05 +0200747 parser = argparse.ArgumentParser(description="VPP unit tests")
juraj.linkes184870a2018-07-16 14:22:01 +0200748 parser.add_argument("-f", "--failfast", action='store_true',
Klement Sekera3f6ff192017-08-11 06:56:05 +0200749 help="fast failure flag")
750 parser.add_argument("-d", "--dir", action='append', type=str,
751 help="directory containing test files "
752 "(may be specified multiple times)")
753 args = parser.parse_args()
juraj.linkes184870a2018-07-16 14:22:01 +0200754 failfast = args.failfast
755 descriptions = True
Klement Sekera3f6ff192017-08-11 06:56:05 +0200756
juraj.linkes184870a2018-07-16 14:22:01 +0200757 print("Running tests using custom test runner") # debug message
758 filter_file, filter_class, filter_func = parse_test_option()
759
760 print("Active filters: file=%s, class=%s, function=%s" % (
761 filter_file, filter_class, filter_func))
762
763 filter_cb = FilterByTestOption(filter_file, filter_class, filter_func)
764
Klement Sekerab8c72a42018-11-08 11:21:39 +0100765 ignore_path = os.getenv("VENV_PATH", None)
juraj.linkes184870a2018-07-16 14:22:01 +0200766 cb = SplitToSuitesCallback(filter_cb)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200767 for d in args.dir:
Klement Sekeradf2b9802017-10-05 10:26:03 +0200768 print("Adding tests from directory tree %s" % d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100769 discover_tests(d, cb, ignore_path)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200770
juraj.linkes184870a2018-07-16 14:22:01 +0200771 # suites are not hashable, need to use list
772 suites = []
773 tests_amount = 0
774 for testcase_suite in cb.suites.values():
775 tests_amount += testcase_suite.countTestCases()
776 suites.append(testcase_suite)
Klement Sekerabbfa5fd2018-06-27 13:54:32 +0200777
juraj.linkes184870a2018-07-16 14:22:01 +0200778 print("%s out of %s tests match specified filters" % (
779 tests_amount, tests_amount + cb.filtered.countTestCases()))
780
781 if not running_extended_tests():
782 print("Not running extended tests (some tests will be skipped)")
783
Klement Sekeradf2b9802017-10-05 10:26:03 +0200784 attempts = retries + 1
785 if attempts > 1:
786 print("Perform %s attempts to pass the suite..." % attempts)
juraj.linkes184870a2018-07-16 14:22:01 +0200787
788 if run_interactive:
789 # don't fork if requiring interactive terminal
juraj.linkesabec0122018-11-16 17:28:56 +0100790 result = VppTestRunner(verbosity=verbose,
791 failfast=failfast,
792 print_summary=True).run(suites[0])
juraj.linkes40dd73b2018-09-21 13:55:16 +0200793 was_successful = result.wasSuccessful()
794 if not was_successful:
795 for test_case_info in result.failed_test_cases_info:
796 handle_failed_suite(test_case_info.logger,
797 test_case_info.tempdir,
798 test_case_info.vpp_pid)
799 if debug_core and \
800 test_case_info in result.core_crash_test_cases_info:
801 check_and_handle_core(test_case_info.vpp_bin_path,
802 test_case_info.tempdir,
803 test_case_info.core_crash_test)
804
805 sys.exit(not was_successful)
Klement Sekera13a83ef2018-03-21 12:35:51 +0100806 else:
juraj.linkes184870a2018-07-16 14:22:01 +0200807 exit_code = 0
808 while len(suites) > 0 and attempts > 0:
juraj.linkes184870a2018-07-16 14:22:01 +0200809 results = run_forked(suites)
810 exit_code, suites = parse_results(results)
811 attempts -= 1
812 if exit_code == 0:
813 print('Test run was successful')
814 else:
815 print('%s attempt(s) left.' % attempts)
816 sys.exit(exit_code)