blob: aee05c65157a29c355e6c5ddea7795f34d8a2bec [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
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -040017import framework
juraj.linkes184870a2018-07-16 14:22:01 +020018from framework import VppTestRunner, running_extended_tests, VppTestCase, \
Ole Trøan5ba91592018-11-22 10:01:09 +000019 get_testcase_doc_name, get_test_description, PASS, FAIL, ERROR, SKIP, \
juraj.linkescae64f82018-09-19 15:01:47 +020020 TEST_RUN
Klement Sekera909a6a12017-08-08 04:33:53 +020021from debug import spawn_gdb
juraj.linkes184870a2018-07-16 14:22:01 +020022from log import get_parallel_logger, double_line_delim, RED, YELLOW, GREEN, \
juraj.linkes40dd73b2018-09-21 13:55:16 +020023 colorize, single_line_delim
Klement Sekerafcbf4442017-08-17 07:38:42 +020024from discover_tests import discover_tests
Klement Sekera9b6ece72018-03-23 10:50:11 +010025from subprocess import check_output, CalledProcessError
juraj.linkes40dd73b2018-09-21 13:55:16 +020026from util import check_core_path, get_core_path, is_core_present
Klement Sekera993e0ed2017-03-16 09:14:59 +010027
Klement Sekera05742262018-03-14 18:14:49 +010028# timeout which controls how long the child has to finish after seeing
29# a core dump in test temporary directory. If this is exceeded, parent assumes
30# that child process is stuck (e.g. waiting for shm mutex, which will never
31# get unlocked) and kill the child
32core_timeout = 3
juraj.linkes184870a2018-07-16 14:22:01 +020033min_req_shm = 536870912 # min 512MB shm required
34# 128MB per extra process
35shm_per_process = 134217728
Klement Sekera05742262018-03-14 18:14:49 +010036
Klement Sekera909a6a12017-08-08 04:33:53 +020037
juraj.linkes184870a2018-07-16 14:22:01 +020038class StreamQueue(Queue):
39 def write(self, msg):
40 self.put(msg)
41
42 def flush(self):
43 sys.__stdout__.flush()
44 sys.__stderr__.flush()
45
46 def fileno(self):
47 return self._writer.fileno()
48
49
50class StreamQueueManager(BaseManager):
51 pass
52
53
juraj.linkescae64f82018-09-19 15:01:47 +020054StreamQueueManager.register('StreamQueue', StreamQueue)
juraj.linkes184870a2018-07-16 14:22:01 +020055
56
juraj.linkescae64f82018-09-19 15:01:47 +020057class TestResult(dict):
juraj.linkes40dd73b2018-09-21 13:55:16 +020058 def __init__(self, testcase_suite, testcases_by_id=None):
juraj.linkescae64f82018-09-19 15:01:47 +020059 super(TestResult, self).__init__()
60 self[PASS] = []
61 self[FAIL] = []
62 self[ERROR] = []
63 self[SKIP] = []
64 self[TEST_RUN] = []
juraj.linkes40dd73b2018-09-21 13:55:16 +020065 self.crashed = False
juraj.linkescae64f82018-09-19 15:01:47 +020066 self.testcase_suite = testcase_suite
67 self.testcases = [testcase for testcase in testcase_suite]
juraj.linkes40dd73b2018-09-21 13:55:16 +020068 self.testcases_by_id = testcases_by_id
juraj.linkescae64f82018-09-19 15:01:47 +020069
70 def was_successful(self):
juraj.linkes40dd73b2018-09-21 13:55:16 +020071 return 0 == len(self[FAIL]) == len(self[ERROR]) \
Klement Sekerab8c72a42018-11-08 11:21:39 +010072 and len(self[PASS] + self[SKIP]) \
73 == self.testcase_suite.countTestCases() == len(self[TEST_RUN])
juraj.linkescae64f82018-09-19 15:01:47 +020074
75 def no_tests_run(self):
76 return 0 == len(self[TEST_RUN])
77
78 def process_result(self, test_id, result):
79 self[result].append(test_id)
juraj.linkescae64f82018-09-19 15:01:47 +020080
81 def suite_from_failed(self):
82 rerun_ids = set([])
83 for testcase in self.testcase_suite:
84 tc_id = testcase.id()
85 if tc_id not in self[PASS] and tc_id not in self[SKIP]:
86 rerun_ids.add(tc_id)
Naveen Joy2cbf2fb2019-03-06 10:41:06 -080087 if rerun_ids:
juraj.linkescae64f82018-09-19 15:01:47 +020088 return suite_from_failed(self.testcase_suite, rerun_ids)
89
90 def get_testcase_names(self, test_id):
juraj.linkes2eca70d2018-12-13 11:10:47 +010091 # could be tearDownClass (test_ipsec_esp.TestIpsecEsp1)
92 setup_teardown_match = re.match(
93 r'((tearDownClass)|(setUpClass)) \((.+\..+)\)', test_id)
94 if setup_teardown_match:
95 test_name, _, _, testcase_name = setup_teardown_match.groups()
96 if len(testcase_name.split('.')) == 2:
97 for key in self.testcases_by_id.keys():
98 if key.startswith(testcase_name):
99 testcase_name = key
100 break
101 testcase_name = self._get_testcase_doc_name(testcase_name)
102 else:
Ole Trøan5ba91592018-11-22 10:01:09 +0000103 test_name = self._get_test_description(test_id)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200104 testcase_name = self._get_testcase_doc_name(test_id)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200105
106 return testcase_name, test_name
juraj.linkescae64f82018-09-19 15:01:47 +0200107
Ole Trøan5ba91592018-11-22 10:01:09 +0000108 def _get_test_description(self, test_id):
juraj.linkes2eca70d2018-12-13 11:10:47 +0100109 if test_id in self.testcases_by_id:
110 desc = get_test_description(descriptions,
111 self.testcases_by_id[test_id])
112 else:
113 desc = test_id
114 return desc
Ole Trøan5ba91592018-11-22 10:01:09 +0000115
juraj.linkes40dd73b2018-09-21 13:55:16 +0200116 def _get_testcase_doc_name(self, test_id):
juraj.linkes2eca70d2018-12-13 11:10:47 +0100117 if test_id in self.testcases_by_id:
118 doc_name = get_testcase_doc_name(self.testcases_by_id[test_id])
119 else:
120 doc_name = test_id
121 return doc_name
juraj.linkescae64f82018-09-19 15:01:47 +0200122
123
124def test_runner_wrapper(suite, keep_alive_pipe, stdouterr_queue,
125 finished_pipe, result_pipe, logger):
juraj.linkes184870a2018-07-16 14:22:01 +0200126 sys.stdout = stdouterr_queue
127 sys.stderr = stdouterr_queue
juraj.linkesdfb5f2a2018-11-09 11:58:54 +0100128 VppTestCase.parallel_handler = logger.handlers[0]
juraj.linkes184870a2018-07-16 14:22:01 +0200129 result = VppTestRunner(keep_alive_pipe=keep_alive_pipe,
130 descriptions=descriptions,
131 verbosity=verbose,
juraj.linkescae64f82018-09-19 15:01:47 +0200132 result_pipe=result_pipe,
juraj.linkesabec0122018-11-16 17:28:56 +0100133 failfast=failfast,
134 print_summary=False).run(suite)
juraj.linkescae64f82018-09-19 15:01:47 +0200135 finished_pipe.send(result.wasSuccessful())
136 finished_pipe.close()
Klement Sekera909a6a12017-08-08 04:33:53 +0200137 keep_alive_pipe.close()
138
139
juraj.linkes184870a2018-07-16 14:22:01 +0200140class TestCaseWrapper(object):
141 def __init__(self, testcase_suite, manager):
142 self.keep_alive_parent_end, self.keep_alive_child_end = Pipe(
143 duplex=False)
juraj.linkescae64f82018-09-19 15:01:47 +0200144 self.finished_parent_end, self.finished_child_end = Pipe(duplex=False)
juraj.linkes184870a2018-07-16 14:22:01 +0200145 self.result_parent_end, self.result_child_end = Pipe(duplex=False)
146 self.testcase_suite = testcase_suite
Ole Troan7f991832018-12-06 17:35:12 +0100147 if sys.version[0] == '2':
148 self.stdouterr_queue = manager.StreamQueue()
149 else:
150 from multiprocessing import get_context
151 self.stdouterr_queue = manager.StreamQueue(ctx=get_context())
juraj.linkes184870a2018-07-16 14:22:01 +0200152 self.logger = get_parallel_logger(self.stdouterr_queue)
153 self.child = Process(target=test_runner_wrapper,
juraj.linkescae64f82018-09-19 15:01:47 +0200154 args=(testcase_suite,
155 self.keep_alive_child_end,
156 self.stdouterr_queue,
157 self.finished_child_end,
158 self.result_child_end,
159 self.logger)
juraj.linkes184870a2018-07-16 14:22:01 +0200160 )
161 self.child.start()
juraj.linkes184870a2018-07-16 14:22:01 +0200162 self.last_test_temp_dir = None
163 self.last_test_vpp_binary = None
juraj.linkes40dd73b2018-09-21 13:55:16 +0200164 self._last_test = None
165 self.last_test_id = None
juraj.linkes721872e2018-09-05 18:13:45 +0200166 self.vpp_pid = None
juraj.linkes184870a2018-07-16 14:22:01 +0200167 self.last_heard = time.time()
168 self.core_detected_at = None
juraj.linkes40dd73b2018-09-21 13:55:16 +0200169 self.testcases_by_id = {}
170 self.testclasess_with_core = {}
171 for testcase in self.testcase_suite:
172 self.testcases_by_id[testcase.id()] = testcase
173 self.result = TestResult(testcase_suite, self.testcases_by_id)
174
175 @property
176 def last_test(self):
177 return self._last_test
178
179 @last_test.setter
180 def last_test(self, test_id):
181 self.last_test_id = test_id
182 if test_id in self.testcases_by_id:
183 testcase = self.testcases_by_id[test_id]
184 self._last_test = testcase.shortDescription()
185 if not self._last_test:
186 self._last_test = str(testcase)
187 else:
188 self._last_test = test_id
189
190 def add_testclass_with_core(self):
191 if self.last_test_id in self.testcases_by_id:
192 test = self.testcases_by_id[self.last_test_id]
193 class_name = unittest.util.strclass(test.__class__)
194 test_name = "'{}' ({})".format(get_test_description(descriptions,
195 test),
196 self.last_test_id)
197 else:
198 test_name = self.last_test_id
199 class_name = re.match(r'((tearDownClass)|(setUpClass)) '
200 r'\((.+\..+)\)', test_name).groups()[3]
201 if class_name not in self.testclasess_with_core:
202 self.testclasess_with_core[class_name] = (
203 test_name,
204 self.last_test_vpp_binary,
205 self.last_test_temp_dir)
juraj.linkes184870a2018-07-16 14:22:01 +0200206
207 def close_pipes(self):
208 self.keep_alive_child_end.close()
juraj.linkescae64f82018-09-19 15:01:47 +0200209 self.finished_child_end.close()
juraj.linkes184870a2018-07-16 14:22:01 +0200210 self.result_child_end.close()
211 self.keep_alive_parent_end.close()
juraj.linkescae64f82018-09-19 15:01:47 +0200212 self.finished_parent_end.close()
juraj.linkes184870a2018-07-16 14:22:01 +0200213 self.result_parent_end.close()
214
juraj.linkes40dd73b2018-09-21 13:55:16 +0200215 def was_successful(self):
216 return self.result.was_successful()
217
juraj.linkes184870a2018-07-16 14:22:01 +0200218
219def stdouterr_reader_wrapper(unread_testcases, finished_unread_testcases,
220 read_testcases):
221 read_testcase = None
Naveen Joy2cbf2fb2019-03-06 10:41:06 -0800222 while read_testcases.is_set() or unread_testcases:
223 if finished_unread_testcases:
juraj.linkese6b58cf2018-11-29 09:56:35 +0100224 read_testcase = finished_unread_testcases.pop()
225 unread_testcases.remove(read_testcase)
Naveen Joy2cbf2fb2019-03-06 10:41:06 -0800226 elif unread_testcases:
juraj.linkese6b58cf2018-11-29 09:56:35 +0100227 read_testcase = unread_testcases.pop()
juraj.linkes184870a2018-07-16 14:22:01 +0200228 if read_testcase:
229 data = ''
230 while data is not None:
231 sys.stdout.write(data)
232 data = read_testcase.stdouterr_queue.get()
233
234 read_testcase.stdouterr_queue.close()
235 finished_unread_testcases.discard(read_testcase)
236 read_testcase = None
237
238
juraj.linkes40dd73b2018-09-21 13:55:16 +0200239def handle_failed_suite(logger, last_test_temp_dir, vpp_pid):
240 if last_test_temp_dir:
241 # Need to create link in case of a timeout or core dump without failure
242 lttd = os.path.basename(last_test_temp_dir)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100243 failed_dir = os.getenv('FAILED_DIR')
juraj.linkes40dd73b2018-09-21 13:55:16 +0200244 link_path = '%s%s-FAILED' % (failed_dir, lttd)
245 if not os.path.exists(link_path):
juraj.linkes40dd73b2018-09-21 13:55:16 +0200246 os.symlink(last_test_temp_dir, link_path)
juraj.linkesabec0122018-11-16 17:28:56 +0100247 logger.error("Symlink to failed testcase directory: %s -> %s"
248 % (link_path, lttd))
juraj.linkes40dd73b2018-09-21 13:55:16 +0200249
250 # Report core existence
251 core_path = get_core_path(last_test_temp_dir)
252 if os.path.exists(core_path):
253 logger.error(
254 "Core-file exists in test temporary directory: %s!" %
255 core_path)
256 check_core_path(logger, core_path)
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800257 logger.debug("Running 'file %s':" % core_path)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200258 try:
259 info = check_output(["file", core_path])
260 logger.debug(info)
261 except CalledProcessError as e:
Paul Vinciguerra38a4ec72018-11-28 11:34:21 -0800262 logger.error("Subprocess returned with return code "
263 "while running `file' utility on core-file "
264 "returned: "
265 "rc=%s", e.returncode)
266 except OSError as e:
267 logger.error("Subprocess returned with OS error while "
268 "running 'file' utility "
269 "on core-file: "
270 "(%s) %s", e.errno, e.strerror)
271 except Exception as e:
272 logger.exception("Unexpected error running `file' utility "
273 "on core-file")
juraj.linkes40dd73b2018-09-21 13:55:16 +0200274
275 if vpp_pid:
276 # Copy api post mortem
277 api_post_mortem_path = "/tmp/api_post_mortem.%d" % vpp_pid
278 if os.path.isfile(api_post_mortem_path):
279 logger.error("Copying api_post_mortem.%d to %s" %
280 (vpp_pid, last_test_temp_dir))
281 shutil.copy2(api_post_mortem_path, last_test_temp_dir)
282
283
284def check_and_handle_core(vpp_binary, tempdir, core_crash_test):
285 if is_core_present(tempdir):
Klement Sekeraf40ee3a2019-05-06 19:11:25 +0200286 if debug_core:
287 print('VPP core detected in %s. Last test running was %s' %
288 (tempdir, core_crash_test))
289 print(single_line_delim)
290 spawn_gdb(vpp_binary, get_core_path(tempdir))
291 print(single_line_delim)
292 elif compress_core:
293 print("Compressing core-file in test directory `%s'" % tempdir)
294 os.system("gzip %s" % get_core_path(tempdir))
juraj.linkes40dd73b2018-09-21 13:55:16 +0200295
296
297def handle_cores(failed_testcases):
Klement Sekeraf40ee3a2019-05-06 19:11:25 +0200298 for failed_testcase in failed_testcases:
299 tcs_with_core = failed_testcase.testclasess_with_core
300 if tcs_with_core:
301 for test, vpp_binary, tempdir in tcs_with_core.values():
302 check_and_handle_core(vpp_binary, tempdir, test)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200303
304
305def process_finished_testsuite(wrapped_testcase_suite,
306 finished_testcase_suites,
307 failed_wrapped_testcases,
308 results):
309 results.append(wrapped_testcase_suite.result)
310 finished_testcase_suites.add(wrapped_testcase_suite)
311 stop_run = False
312 if failfast and not wrapped_testcase_suite.was_successful():
313 stop_run = True
314
315 if not wrapped_testcase_suite.was_successful():
316 failed_wrapped_testcases.add(wrapped_testcase_suite)
317 handle_failed_suite(wrapped_testcase_suite.logger,
318 wrapped_testcase_suite.last_test_temp_dir,
319 wrapped_testcase_suite.vpp_pid)
320
321 return stop_run
322
323
juraj.linkes721872e2018-09-05 18:13:45 +0200324def run_forked(testcase_suites):
juraj.linkes184870a2018-07-16 14:22:01 +0200325 wrapped_testcase_suites = set()
326
327 # suites are unhashable, need to use list
328 results = []
juraj.linkes184870a2018-07-16 14:22:01 +0200329 unread_testcases = set()
330 finished_unread_testcases = set()
331 manager = StreamQueueManager()
332 manager.start()
333 for i in range(concurrent_tests):
Naveen Joy2cbf2fb2019-03-06 10:41:06 -0800334 if testcase_suites:
juraj.linkes721872e2018-09-05 18:13:45 +0200335 wrapped_testcase_suite = TestCaseWrapper(testcase_suites.pop(0),
336 manager)
juraj.linkes184870a2018-07-16 14:22:01 +0200337 wrapped_testcase_suites.add(wrapped_testcase_suite)
338 unread_testcases.add(wrapped_testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200339 else:
340 break
341
342 read_from_testcases = threading.Event()
343 read_from_testcases.set()
344 stdouterr_thread = threading.Thread(target=stdouterr_reader_wrapper,
345 args=(unread_testcases,
346 finished_unread_testcases,
347 read_from_testcases))
348 stdouterr_thread.start()
349
juraj.linkes40dd73b2018-09-21 13:55:16 +0200350 failed_wrapped_testcases = set()
351 stop_run = False
juraj.linkese6b58cf2018-11-29 09:56:35 +0100352
353 try:
Naveen Joy2cbf2fb2019-03-06 10:41:06 -0800354 while wrapped_testcase_suites:
juraj.linkese6b58cf2018-11-29 09:56:35 +0100355 finished_testcase_suites = set()
356 for wrapped_testcase_suite in wrapped_testcase_suites:
357 while wrapped_testcase_suite.result_parent_end.poll():
358 wrapped_testcase_suite.result.process_result(
359 *wrapped_testcase_suite.result_parent_end.recv())
360 wrapped_testcase_suite.last_heard = time.time()
361
362 while wrapped_testcase_suite.keep_alive_parent_end.poll():
363 wrapped_testcase_suite.last_test, \
364 wrapped_testcase_suite.last_test_vpp_binary, \
365 wrapped_testcase_suite.last_test_temp_dir, \
366 wrapped_testcase_suite.vpp_pid = \
367 wrapped_testcase_suite.keep_alive_parent_end.recv()
368 wrapped_testcase_suite.last_heard = time.time()
369
370 if wrapped_testcase_suite.finished_parent_end.poll():
371 wrapped_testcase_suite.finished_parent_end.recv()
372 wrapped_testcase_suite.last_heard = time.time()
373 stop_run = process_finished_testsuite(
374 wrapped_testcase_suite,
375 finished_testcase_suites,
376 failed_wrapped_testcases,
377 results) or stop_run
378 continue
379
380 fail = False
381 if wrapped_testcase_suite.last_heard + test_timeout < \
382 time.time():
383 fail = True
384 wrapped_testcase_suite.logger.critical(
385 "Child test runner process timed out "
386 "(last test running was `%s' in `%s')!" %
387 (wrapped_testcase_suite.last_test,
388 wrapped_testcase_suite.last_test_temp_dir))
389 elif not wrapped_testcase_suite.child.is_alive():
390 fail = True
391 wrapped_testcase_suite.logger.critical(
392 "Child test runner process unexpectedly died "
393 "(last test running was `%s' in `%s')!" %
394 (wrapped_testcase_suite.last_test,
395 wrapped_testcase_suite.last_test_temp_dir))
396 elif wrapped_testcase_suite.last_test_temp_dir and \
397 wrapped_testcase_suite.last_test_vpp_binary:
398 if is_core_present(
399 wrapped_testcase_suite.last_test_temp_dir):
400 wrapped_testcase_suite.add_testclass_with_core()
401 if wrapped_testcase_suite.core_detected_at is None:
402 wrapped_testcase_suite.core_detected_at = \
403 time.time()
404 elif wrapped_testcase_suite.core_detected_at + \
405 core_timeout < time.time():
406 wrapped_testcase_suite.logger.critical(
407 "Child test runner process unresponsive and "
408 "core-file exists in test temporary directory "
409 "(last test running was `%s' in `%s')!" %
410 (wrapped_testcase_suite.last_test,
411 wrapped_testcase_suite.last_test_temp_dir))
412 fail = True
413
414 if fail:
415 wrapped_testcase_suite.child.terminate()
416 try:
417 # terminating the child process tends to leave orphan
418 # VPP process around
419 if wrapped_testcase_suite.vpp_pid:
420 os.kill(wrapped_testcase_suite.vpp_pid,
421 signal.SIGTERM)
422 except OSError:
423 # already dead
424 pass
425 wrapped_testcase_suite.result.crashed = True
426 wrapped_testcase_suite.result.process_result(
427 wrapped_testcase_suite.last_test_id, ERROR)
428 stop_run = process_finished_testsuite(
429 wrapped_testcase_suite,
430 finished_testcase_suites,
431 failed_wrapped_testcases,
432 results) or stop_run
433
434 for finished_testcase in finished_testcase_suites:
435 finished_testcase.child.join()
436 finished_testcase.close_pipes()
437 wrapped_testcase_suites.remove(finished_testcase)
438 finished_unread_testcases.add(finished_testcase)
439 finished_testcase.stdouterr_queue.put(None)
440 if stop_run:
Naveen Joy2cbf2fb2019-03-06 10:41:06 -0800441 while testcase_suites:
juraj.linkese6b58cf2018-11-29 09:56:35 +0100442 results.append(TestResult(testcase_suites.pop(0)))
Naveen Joy2cbf2fb2019-03-06 10:41:06 -0800443 elif testcase_suites:
juraj.linkese6b58cf2018-11-29 09:56:35 +0100444 new_testcase = TestCaseWrapper(testcase_suites.pop(0),
445 manager)
446 wrapped_testcase_suites.add(new_testcase)
447 unread_testcases.add(new_testcase)
Paul Vinciguerrac0692a42019-03-15 19:16:50 -0700448 time.sleep(0.1)
juraj.linkese6b58cf2018-11-29 09:56:35 +0100449 except Exception:
juraj.linkes184870a2018-07-16 14:22:01 +0200450 for wrapped_testcase_suite in wrapped_testcase_suites:
juraj.linkese6b58cf2018-11-29 09:56:35 +0100451 wrapped_testcase_suite.child.terminate()
452 wrapped_testcase_suite.stdouterr_queue.put(None)
453 raise
454 finally:
455 read_from_testcases.clear()
456 stdouterr_thread.join(test_timeout)
457 manager.shutdown()
juraj.linkescae64f82018-09-19 15:01:47 +0200458
juraj.linkes40dd73b2018-09-21 13:55:16 +0200459 handle_cores(failed_wrapped_testcases)
juraj.linkes184870a2018-07-16 14:22:01 +0200460 return results
461
462
463class SplitToSuitesCallback:
464 def __init__(self, filter_callback):
465 self.suites = {}
466 self.suite_name = 'default'
467 self.filter_callback = filter_callback
468 self.filtered = unittest.TestSuite()
Klement Sekerafcbf4442017-08-17 07:38:42 +0200469
470 def __call__(self, file_name, cls, method):
juraj.linkes184870a2018-07-16 14:22:01 +0200471 test_method = cls(method)
472 if self.filter_callback(file_name, cls.__name__, method):
473 self.suite_name = file_name + cls.__name__
474 if self.suite_name not in self.suites:
475 self.suites[self.suite_name] = unittest.TestSuite()
476 self.suites[self.suite_name].addTest(test_method)
477
478 else:
479 self.filtered.addTest(test_method)
Klement Sekerafcbf4442017-08-17 07:38:42 +0200480
481
juraj.linkes184870a2018-07-16 14:22:01 +0200482test_option = "TEST"
483
484
485def parse_test_option():
486 f = os.getenv(test_option, None)
487 filter_file_name = None
488 filter_class_name = None
489 filter_func_name = None
490 if f:
491 if '.' in f:
492 parts = f.split('.')
493 if len(parts) > 3:
494 raise Exception("Unrecognized %s option: %s" %
495 (test_option, f))
496 if len(parts) > 2:
497 if parts[2] not in ('*', ''):
498 filter_func_name = parts[2]
499 if parts[1] not in ('*', ''):
500 filter_class_name = parts[1]
501 if parts[0] not in ('*', ''):
502 if parts[0].startswith('test_'):
503 filter_file_name = parts[0]
504 else:
505 filter_file_name = 'test_%s' % parts[0]
506 else:
507 if f.startswith('test_'):
508 filter_file_name = f
509 else:
510 filter_file_name = 'test_%s' % f
511 if filter_file_name:
512 filter_file_name = '%s.py' % filter_file_name
513 return filter_file_name, filter_class_name, filter_func_name
514
515
516def filter_tests(tests, filter_cb):
517 result = unittest.suite.TestSuite()
518 for t in tests:
519 if isinstance(t, unittest.suite.TestSuite):
520 # this is a bunch of tests, recursively filter...
521 x = filter_tests(t, filter_cb)
522 if x.countTestCases() > 0:
523 result.addTest(x)
524 elif isinstance(t, unittest.TestCase):
525 # this is a single test
526 parts = t.id().split('.')
527 # t.id() for common cases like this:
528 # test_classifier.TestClassifier.test_acl_ip
529 # apply filtering only if it is so
530 if len(parts) == 3:
531 if not filter_cb(parts[0], parts[1], parts[2]):
532 continue
533 result.addTest(t)
534 else:
535 # unexpected object, don't touch it
536 result.addTest(t)
537 return result
538
539
540class FilterByTestOption:
541 def __init__(self, filter_file_name, filter_class_name, filter_func_name):
542 self.filter_file_name = filter_file_name
543 self.filter_class_name = filter_class_name
544 self.filter_func_name = filter_func_name
545
546 def __call__(self, file_name, class_name, func_name):
Andrew Yourtchenkod760f792018-10-03 11:38:31 +0200547 if self.filter_file_name:
548 fn_match = fnmatch.fnmatch(file_name, self.filter_file_name)
549 if not fn_match:
550 return False
juraj.linkes184870a2018-07-16 14:22:01 +0200551 if self.filter_class_name and class_name != self.filter_class_name:
552 return False
553 if self.filter_func_name and func_name != self.filter_func_name:
554 return False
555 return True
556
557
558class FilterByClassList:
juraj.linkes721872e2018-09-05 18:13:45 +0200559 def __init__(self, classes_with_filenames):
560 self.classes_with_filenames = classes_with_filenames
Klement Sekeradf2b9802017-10-05 10:26:03 +0200561
562 def __call__(self, file_name, class_name, func_name):
juraj.linkes721872e2018-09-05 18:13:45 +0200563 return '.'.join([file_name, class_name]) in self.classes_with_filenames
Klement Sekeradf2b9802017-10-05 10:26:03 +0200564
565
566def suite_from_failed(suite, failed):
juraj.linkes721872e2018-09-05 18:13:45 +0200567 failed = {x.rsplit('.', 1)[0] for x in failed}
juraj.linkes184870a2018-07-16 14:22:01 +0200568 filter_cb = FilterByClassList(failed)
569 suite = filter_tests(suite, filter_cb)
Klement Sekera4c5422e2018-06-22 13:19:45 +0200570 return suite
Klement Sekeradf2b9802017-10-05 10:26:03 +0200571
572
juraj.linkescae64f82018-09-19 15:01:47 +0200573class AllResults(dict):
juraj.linkes184870a2018-07-16 14:22:01 +0200574 def __init__(self):
juraj.linkescae64f82018-09-19 15:01:47 +0200575 super(AllResults, self).__init__()
juraj.linkes184870a2018-07-16 14:22:01 +0200576 self.all_testcases = 0
juraj.linkescae64f82018-09-19 15:01:47 +0200577 self.results_per_suite = []
578 self[PASS] = 0
579 self[FAIL] = 0
580 self[ERROR] = 0
581 self[SKIP] = 0
582 self[TEST_RUN] = 0
juraj.linkes184870a2018-07-16 14:22:01 +0200583 self.rerun = []
juraj.linkescae64f82018-09-19 15:01:47 +0200584 self.testsuites_no_tests_run = []
Klement Sekera909a6a12017-08-08 04:33:53 +0200585
juraj.linkescae64f82018-09-19 15:01:47 +0200586 def add_results(self, result):
587 self.results_per_suite.append(result)
588 result_types = [PASS, FAIL, ERROR, SKIP, TEST_RUN]
589 for result_type in result_types:
590 self[result_type] += len(result[result_type])
Klement Sekera05742262018-03-14 18:14:49 +0100591
juraj.linkescae64f82018-09-19 15:01:47 +0200592 def add_result(self, result):
juraj.linkes184870a2018-07-16 14:22:01 +0200593 retval = 0
juraj.linkescae64f82018-09-19 15:01:47 +0200594 self.all_testcases += result.testcase_suite.countTestCases()
juraj.linkes40dd73b2018-09-21 13:55:16 +0200595 self.add_results(result)
juraj.linkes184870a2018-07-16 14:22:01 +0200596
juraj.linkes40dd73b2018-09-21 13:55:16 +0200597 if result.no_tests_run():
juraj.linkescae64f82018-09-19 15:01:47 +0200598 self.testsuites_no_tests_run.append(result.testcase_suite)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200599 if result.crashed:
600 retval = -1
601 else:
602 retval = 1
603 elif not result.was_successful():
604 retval = 1
juraj.linkes184870a2018-07-16 14:22:01 +0200605
juraj.linkes184870a2018-07-16 14:22:01 +0200606 if retval != 0:
juraj.linkesabec0122018-11-16 17:28:56 +0100607 self.rerun.append(result.testcase_suite)
juraj.linkes184870a2018-07-16 14:22:01 +0200608
609 return retval
610
611 def print_results(self):
612 print('')
613 print(double_line_delim)
614 print('TEST RESULTS:')
juraj.linkescae64f82018-09-19 15:01:47 +0200615 print(' Scheduled tests: {}'.format(self.all_testcases))
616 print(' Executed tests: {}'.format(self[TEST_RUN]))
617 print(' Passed tests: {}'.format(
618 colorize(str(self[PASS]), GREEN)))
619 if self[SKIP] > 0:
620 print(' Skipped tests: {}'.format(
621 colorize(str(self[SKIP]), YELLOW)))
622 if self.not_executed > 0:
623 print(' Not Executed tests: {}'.format(
624 colorize(str(self.not_executed), RED)))
625 if self[FAIL] > 0:
626 print(' Failures: {}'.format(
627 colorize(str(self[FAIL]), RED)))
628 if self[ERROR] > 0:
629 print(' Errors: {}'.format(
630 colorize(str(self[ERROR]), RED)))
juraj.linkes184870a2018-07-16 14:22:01 +0200631
632 if self.all_failed > 0:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200633 print('FAILURES AND ERRORS IN TESTS:')
juraj.linkescae64f82018-09-19 15:01:47 +0200634 for result in self.results_per_suite:
635 failed_testcase_ids = result[FAIL]
636 errored_testcase_ids = result[ERROR]
637 old_testcase_name = None
Naveen Joy2cbf2fb2019-03-06 10:41:06 -0800638 if failed_testcase_ids or errored_testcase_ids:
juraj.linkescae64f82018-09-19 15:01:47 +0200639 for failed_test_id in failed_testcase_ids:
640 new_testcase_name, test_name = \
641 result.get_testcase_names(failed_test_id)
642 if new_testcase_name != old_testcase_name:
643 print(' Testcase name: {}'.format(
644 colorize(new_testcase_name, RED)))
645 old_testcase_name = new_testcase_name
Klement Sekera33177d62018-11-30 14:17:20 +0100646 print(' FAILURE: {} [{}]'.format(
647 colorize(test_name, RED), failed_test_id))
juraj.linkescae64f82018-09-19 15:01:47 +0200648 for failed_test_id in errored_testcase_ids:
649 new_testcase_name, test_name = \
650 result.get_testcase_names(failed_test_id)
651 if new_testcase_name != old_testcase_name:
652 print(' Testcase name: {}'.format(
653 colorize(new_testcase_name, RED)))
654 old_testcase_name = new_testcase_name
Klement Sekera33177d62018-11-30 14:17:20 +0100655 print(' ERROR: {} [{}]'.format(
656 colorize(test_name, RED), failed_test_id))
Naveen Joy2cbf2fb2019-03-06 10:41:06 -0800657 if self.testsuites_no_tests_run:
juraj.linkescae64f82018-09-19 15:01:47 +0200658 print('TESTCASES WHERE NO TESTS WERE SUCCESSFULLY EXECUTED:')
juraj.linkes40dd73b2018-09-21 13:55:16 +0200659 tc_classes = set()
juraj.linkescae64f82018-09-19 15:01:47 +0200660 for testsuite in self.testsuites_no_tests_run:
661 for testcase in testsuite:
662 tc_classes.add(get_testcase_doc_name(testcase))
663 for tc_class in tc_classes:
664 print(' {}'.format(colorize(tc_class, RED)))
juraj.linkes184870a2018-07-16 14:22:01 +0200665
666 print(double_line_delim)
667 print('')
668
669 @property
juraj.linkescae64f82018-09-19 15:01:47 +0200670 def not_executed(self):
671 return self.all_testcases - self[TEST_RUN]
672
673 @property
juraj.linkes184870a2018-07-16 14:22:01 +0200674 def all_failed(self):
juraj.linkescae64f82018-09-19 15:01:47 +0200675 return self[FAIL] + self[ERROR]
juraj.linkes184870a2018-07-16 14:22:01 +0200676
677
678def parse_results(results):
679 """
juraj.linkescae64f82018-09-19 15:01:47 +0200680 Prints the number of scheduled, executed, not executed, passed, failed,
681 errored and skipped tests and details about failed and errored tests.
juraj.linkes184870a2018-07-16 14:22:01 +0200682
juraj.linkescae64f82018-09-19 15:01:47 +0200683 Also returns all suites where any test failed.
juraj.linkes184870a2018-07-16 14:22:01 +0200684
685 :param results:
686 :return:
687 """
688
juraj.linkescae64f82018-09-19 15:01:47 +0200689 results_per_suite = AllResults()
juraj.linkes184870a2018-07-16 14:22:01 +0200690 crashed = False
691 failed = False
juraj.linkescae64f82018-09-19 15:01:47 +0200692 for result in results:
693 result_code = results_per_suite.add_result(result)
juraj.linkes184870a2018-07-16 14:22:01 +0200694 if result_code == 1:
695 failed = True
696 elif result_code == -1:
697 crashed = True
698
699 results_per_suite.print_results()
700
701 if crashed:
702 return_code = -1
703 elif failed:
704 return_code = 1
705 else:
706 return_code = 0
707 return return_code, results_per_suite.rerun
708
709
710def parse_digit_env(env_var, default):
711 value = os.getenv(env_var, default)
712 if value != default:
713 if value.isdigit():
714 value = int(value)
715 else:
716 print('WARNING: unsupported value "%s" for env var "%s",'
717 'defaulting to %s' % (value, env_var, default))
718 value = default
719 return value
Klement Sekera3f6ff192017-08-11 06:56:05 +0200720
721
722if __name__ == '__main__':
723
juraj.linkes184870a2018-07-16 14:22:01 +0200724 verbose = parse_digit_env("V", 0)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200725
juraj.linkes184870a2018-07-16 14:22:01 +0200726 test_timeout = parse_digit_env("TIMEOUT", 600) # default = 10 minutes
Klement Sekera3f6ff192017-08-11 06:56:05 +0200727
juraj.linkes184870a2018-07-16 14:22:01 +0200728 retries = parse_digit_env("RETRIES", 0)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200729
juraj.linkes184870a2018-07-16 14:22:01 +0200730 debug = os.getenv("DEBUG", "n").lower() in ["gdb", "gdbserver"]
731
juraj.linkes40dd73b2018-09-21 13:55:16 +0200732 debug_core = os.getenv("DEBUG", "").lower() == "core"
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400733 compress_core = framework.BoolEnvironmentVariable("CORE_COMPRESS")
juraj.linkes40dd73b2018-09-21 13:55:16 +0200734
Paul Vinciguerra0cbc71d2019-07-03 08:38:38 -0400735 step = framework.BoolEnvironmentVariable("STEP")
736 force_foreground = framework.BoolEnvironmentVariable("FORCE_FOREGROUND")
juraj.linkesb5ef26d2019-07-03 10:42:40 +0200737
738 run_interactive = debug or step or force_foreground
juraj.linkes184870a2018-07-16 14:22:01 +0200739
740 test_jobs = os.getenv("TEST_JOBS", "1").lower() # default = 1 process
741 if test_jobs == 'auto':
742 if run_interactive:
743 concurrent_tests = 1
744 print('Interactive mode required, running on one core')
745 else:
746 shm_free = psutil.disk_usage('/dev/shm').free
747 shm_max_processes = 1
748 if shm_free < min_req_shm:
749 raise Exception('Not enough free space in /dev/shm. Required '
750 'free space is at least %sM.'
751 % (min_req_shm >> 20))
752 else:
753 extra_shm = shm_free - min_req_shm
754 shm_max_processes += extra_shm / shm_per_process
Klement Sekera6c7bda92018-09-18 14:32:27 +0200755 concurrent_tests = min(cpu_count(), shm_max_processes)
juraj.linkes184870a2018-07-16 14:22:01 +0200756 print('Found enough resources to run tests with %s cores'
757 % concurrent_tests)
758 elif test_jobs.isdigit():
759 concurrent_tests = int(test_jobs)
760 else:
761 concurrent_tests = 1
762
763 if run_interactive and concurrent_tests > 1:
764 raise NotImplementedError(
juraj.linkes40dd73b2018-09-21 13:55:16 +0200765 'Running tests interactively (DEBUG is gdb or gdbserver or STEP '
766 'is set) in parallel (TEST_JOBS is more than 1) is not supported')
Klement Sekera13a83ef2018-03-21 12:35:51 +0100767
Klement Sekera3f6ff192017-08-11 06:56:05 +0200768 parser = argparse.ArgumentParser(description="VPP unit tests")
juraj.linkes184870a2018-07-16 14:22:01 +0200769 parser.add_argument("-f", "--failfast", action='store_true',
Klement Sekera3f6ff192017-08-11 06:56:05 +0200770 help="fast failure flag")
771 parser.add_argument("-d", "--dir", action='append', type=str,
772 help="directory containing test files "
773 "(may be specified multiple times)")
774 args = parser.parse_args()
juraj.linkes184870a2018-07-16 14:22:01 +0200775 failfast = args.failfast
776 descriptions = True
Klement Sekera3f6ff192017-08-11 06:56:05 +0200777
juraj.linkes184870a2018-07-16 14:22:01 +0200778 print("Running tests using custom test runner") # debug message
779 filter_file, filter_class, filter_func = parse_test_option()
780
781 print("Active filters: file=%s, class=%s, function=%s" % (
782 filter_file, filter_class, filter_func))
783
784 filter_cb = FilterByTestOption(filter_file, filter_class, filter_func)
785
Klement Sekerab8c72a42018-11-08 11:21:39 +0100786 ignore_path = os.getenv("VENV_PATH", None)
juraj.linkes184870a2018-07-16 14:22:01 +0200787 cb = SplitToSuitesCallback(filter_cb)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200788 for d in args.dir:
Klement Sekeradf2b9802017-10-05 10:26:03 +0200789 print("Adding tests from directory tree %s" % d)
Klement Sekerab8c72a42018-11-08 11:21:39 +0100790 discover_tests(d, cb, ignore_path)
Klement Sekera3f6ff192017-08-11 06:56:05 +0200791
juraj.linkes184870a2018-07-16 14:22:01 +0200792 # suites are not hashable, need to use list
793 suites = []
794 tests_amount = 0
795 for testcase_suite in cb.suites.values():
796 tests_amount += testcase_suite.countTestCases()
797 suites.append(testcase_suite)
Klement Sekerabbfa5fd2018-06-27 13:54:32 +0200798
juraj.linkes184870a2018-07-16 14:22:01 +0200799 print("%s out of %s tests match specified filters" % (
800 tests_amount, tests_amount + cb.filtered.countTestCases()))
801
Paul Vinciguerradefde0f2018-12-06 07:46:13 -0800802 if not running_extended_tests:
juraj.linkes184870a2018-07-16 14:22:01 +0200803 print("Not running extended tests (some tests will be skipped)")
804
Klement Sekeradf2b9802017-10-05 10:26:03 +0200805 attempts = retries + 1
806 if attempts > 1:
807 print("Perform %s attempts to pass the suite..." % attempts)
juraj.linkes184870a2018-07-16 14:22:01 +0200808
Naveen Joy2cbf2fb2019-03-06 10:41:06 -0800809 if run_interactive and suites:
juraj.linkes184870a2018-07-16 14:22:01 +0200810 # don't fork if requiring interactive terminal
juraj.linkesb5ef26d2019-07-03 10:42:40 +0200811 print('Running tests in foreground in the current process')
juraj.linkes46e8e912019-01-10 12:13:07 +0100812 full_suite = unittest.TestSuite()
813 map(full_suite.addTests, suites)
juraj.linkesabec0122018-11-16 17:28:56 +0100814 result = VppTestRunner(verbosity=verbose,
815 failfast=failfast,
juraj.linkes46e8e912019-01-10 12:13:07 +0100816 print_summary=True).run(full_suite)
juraj.linkes40dd73b2018-09-21 13:55:16 +0200817 was_successful = result.wasSuccessful()
818 if not was_successful:
819 for test_case_info in result.failed_test_cases_info:
820 handle_failed_suite(test_case_info.logger,
821 test_case_info.tempdir,
822 test_case_info.vpp_pid)
Klement Sekeraf40ee3a2019-05-06 19:11:25 +0200823 if test_case_info in result.core_crash_test_cases_info:
juraj.linkes40dd73b2018-09-21 13:55:16 +0200824 check_and_handle_core(test_case_info.vpp_bin_path,
825 test_case_info.tempdir,
826 test_case_info.core_crash_test)
827
828 sys.exit(not was_successful)
Klement Sekera13a83ef2018-03-21 12:35:51 +0100829 else:
juraj.linkesb5ef26d2019-07-03 10:42:40 +0200830 print('Running each VPPTestCase in a separate background process'
831 ' with {} parallel process(es)'.format(concurrent_tests))
juraj.linkes184870a2018-07-16 14:22:01 +0200832 exit_code = 0
Naveen Joy2cbf2fb2019-03-06 10:41:06 -0800833 while suites and attempts > 0:
juraj.linkes184870a2018-07-16 14:22:01 +0200834 results = run_forked(suites)
835 exit_code, suites = parse_results(results)
836 attempts -= 1
837 if exit_code == 0:
838 print('Test run was successful')
839 else:
840 print('%s attempt(s) left.' % attempts)
841 sys.exit(exit_code)