blob: 31cc4b749e78cb165fee375bd9fa9f82d950eb51 [file] [log] [blame]
Johnny Chene8d9dc62011-10-31 19:04:07 +00001"""
2Run the test suite using a separate process for each test file.
Vince Harronede59652015-01-08 02:11:26 +00003
Siva Chandra2d7832e2015-05-08 23:08:53 +00004Each test will run with a time limit of 10 minutes by default.
Vince Harronede59652015-01-08 02:11:26 +00005
Siva Chandra2d7832e2015-05-08 23:08:53 +00006Override the default time limit of 10 minutes by setting
Vince Harronede59652015-01-08 02:11:26 +00007the environment variable LLDB_TEST_TIMEOUT.
8
9E.g., export LLDB_TEST_TIMEOUT=10m
10
11Override the time limit for individual tests by setting
12the environment variable LLDB_[TEST NAME]_TIMEOUT.
13
14E.g., export LLDB_TESTCONCURRENTEVENTS_TIMEOUT=2m
15
16Set to "0" to run without time limit.
17
18E.g., export LLDB_TEST_TIMEOUT=0
19or export LLDB_TESTCONCURRENTEVENTS_TIMEOUT=0
Vince Harrondcc2b9f2015-05-27 04:40:36 +000020
Chaoren Linb6325d02015-08-12 18:02:54 +000021To collect core files for timed out tests,
22do the following before running dosep.py
Vince Harrondcc2b9f2015-05-27 04:40:36 +000023
24OSX
25ulimit -c unlimited
26sudo sysctl -w kern.corefile=core.%P
27
28Linux:
29ulimit -c unlimited
30echo core.%p | sudo tee /proc/sys/kernel/core_pattern
Johnny Chene8d9dc62011-10-31 19:04:07 +000031"""
32
Zachary Turnerff890da2015-10-19 23:45:41 +000033from __future__ import print_function
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000034from __future__ import absolute_import
Zachary Turner814236d2015-10-21 17:48:52 +000035
Todd Fiala2d3754d2015-09-29 22:19:06 +000036# system packages and modules
Todd Fiala68615ce2015-09-15 21:38:04 +000037import asyncore
Todd Fiala83c32e32015-09-22 21:19:40 +000038import distutils.version
Vince Harrondcc2b9f2015-05-27 04:40:36 +000039import fnmatch
Todd Fiala8cbeed32015-09-08 22:22:33 +000040import multiprocessing
41import multiprocessing.pool
42import os
Todd Fiala3f0a3602014-07-08 06:42:37 +000043import platform
Vince Harron06381732015-05-12 23:10:36 +000044import re
Todd Fiala8cbeed32015-09-08 22:22:33 +000045import signal
Todd Fiala3f0a3602014-07-08 06:42:37 +000046import sys
Todd Fiala8cbeed32015-09-08 22:22:33 +000047import threading
Todd Fiala2d3754d2015-09-29 22:19:06 +000048
Zachary Turner814236d2015-10-21 17:48:52 +000049from six.moves import queue
50
Todd Fiala2d3754d2015-09-29 22:19:06 +000051# Our packages and modules
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000052import lldbsuite.support.seven as seven
53
54from . import dotest_channels
55from . import dotest_args
Zachary Turner905a9882015-12-07 21:23:41 +000056from . import result_formatter
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000057
Todd Fiala51831472015-12-09 06:45:43 +000058from .result_formatter import EventBuilder
59
60
61# Todo: Convert this folder layout to be relative-import friendly and
62# don't hack up sys.path like this
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000063sys.path.append(os.path.join(os.path.dirname(__file__), "test_runner", "lib"))
Todd Fiala2d3754d2015-09-29 22:19:06 +000064import lldb_utils
65import process_control
Vince Harron17f429f2014-12-13 00:08:19 +000066
Vince Harron17f429f2014-12-13 00:08:19 +000067# Status codes for running command with timeout.
68eTimedOut, ePassed, eFailed = 124, 0, 1
69
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000070output_lock = None
71test_counter = None
72total_tests = None
Chaoren Linffc63b02015-08-12 18:02:49 +000073test_name_len = None
Pavel Labath05ab2372015-07-06 15:57:52 +000074dotest_options = None
Todd Fiala68615ce2015-09-15 21:38:04 +000075RESULTS_FORMATTER = None
76RUNNER_PROCESS_ASYNC_MAP = None
77RESULTS_LISTENER_CHANNEL = None
Chaoren Linb6325d02015-08-12 18:02:54 +000078
Todd Fiala33896a92015-09-18 21:01:13 +000079"""Contains an optional function pointer that can return the worker index
80 for the given thread/process calling it. Returns a 0-based index."""
81GET_WORKER_INDEX = None
82
Todd Fiala1cc97b42015-09-21 05:42:26 +000083
Todd Fiala33896a92015-09-18 21:01:13 +000084def setup_global_variables(
85 lock, counter, total, name_len, options, worker_index_map):
Chaoren Linffc63b02015-08-12 18:02:49 +000086 global output_lock, test_counter, total_tests, test_name_len
87 global dotest_options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000088 output_lock = lock
89 test_counter = counter
90 total_tests = total
Chaoren Linffc63b02015-08-12 18:02:49 +000091 test_name_len = name_len
Pavel Labath05ab2372015-07-06 15:57:52 +000092 dotest_options = options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000093
Todd Fiala33896a92015-09-18 21:01:13 +000094 if worker_index_map is not None:
95 # We'll use the output lock for this to avoid sharing another lock.
96 # This won't be used much.
97 index_lock = lock
98
99 def get_worker_index_use_pid():
100 """Returns a 0-based, process-unique index for the worker."""
101 pid = os.getpid()
102 with index_lock:
103 if pid not in worker_index_map:
104 worker_index_map[pid] = len(worker_index_map)
105 return worker_index_map[pid]
106
107 global GET_WORKER_INDEX
108 GET_WORKER_INDEX = get_worker_index_use_pid
109
Zachary Turner38e64172015-08-10 17:46:11 +0000110def report_test_failure(name, command, output):
111 global output_lock
112 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000113 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
Zachary Turnerff890da2015-10-19 23:45:41 +0000114 print(file=sys.stderr)
115 print(output, file=sys.stderr)
116 print("[%s FAILED]" % name, file=sys.stderr)
117 print("Command invoked: %s" % ' '.join(command), file=sys.stderr)
Chaoren Linffc63b02015-08-12 18:02:49 +0000118 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000119
Chaoren Linb6325d02015-08-12 18:02:54 +0000120
Zachary Turner38e64172015-08-10 17:46:11 +0000121def report_test_pass(name, output):
Zachary Turner80310c22015-12-10 18:51:02 +0000122 global output_lock
Zachary Turner38e64172015-08-10 17:46:11 +0000123 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000124 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000125
Chaoren Linb6325d02015-08-12 18:02:54 +0000126
Chaoren Linffc63b02015-08-12 18:02:49 +0000127def update_progress(test_name=""):
128 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000129 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000130 counter_len = len(str(total_tests))
Greg Clayton1827fc22015-09-19 00:39:09 +0000131 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
132 sys.stderr.write(
133 "\r%*d out of %d test suites processed - %-*s" %
134 (counter_len, test_counter.value, total_tests,
135 test_name_len.value, test_name))
Chaoren Linffc63b02015-08-12 18:02:49 +0000136 if len(test_name) > test_name_len.value:
137 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000138 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000139 sys.stdout.flush()
140 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000141
Chaoren Linb6325d02015-08-12 18:02:54 +0000142
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000143def parse_test_results(output):
144 passes = 0
145 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000146 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000147 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000148 pass_count = re.search("^RESULT:.*([0-9]+) passes",
149 result, re.MULTILINE)
150 fail_count = re.search("^RESULT:.*([0-9]+) failures",
151 result, re.MULTILINE)
152 error_count = re.search("^RESULT:.*([0-9]+) errors",
153 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000154 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
155 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000156 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000157 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000158 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000159 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000160 if unexpected_success_count is not None:
161 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000162 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000163 failures = failures + int(error_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000164 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000165
Chaoren Linb6325d02015-08-12 18:02:54 +0000166
Todd Fiala2d3754d2015-09-29 22:19:06 +0000167class DoTestProcessDriver(process_control.ProcessDriver):
168 """Drives the dotest.py inferior process and handles bookkeeping."""
169 def __init__(self, output_file, output_file_lock, pid_events, file_name,
170 soft_terminate_timeout):
171 super(DoTestProcessDriver, self).__init__(
172 soft_terminate_timeout=soft_terminate_timeout)
173 self.output_file = output_file
174 self.output_lock = lldb_utils.OptionalWith(output_file_lock)
175 self.pid_events = pid_events
176 self.results = None
177 self.file_name = file_name
178
179 def write(self, content):
180 with self.output_lock:
181 self.output_file.write(content)
182
183 def on_process_started(self):
184 if self.pid_events:
185 self.pid_events.put_nowait(('created', self.process.pid))
186
187 def on_process_exited(self, command, output, was_timeout, exit_status):
188 if self.pid_events:
189 # No point in culling out those with no exit_status (i.e.
190 # those we failed to kill). That would just cause
191 # downstream code to try to kill it later on a Ctrl-C. At
192 # this point, a best-effort-to-kill already took place. So
193 # call it destroyed here.
194 self.pid_events.put_nowait(('destroyed', self.process.pid))
195
196 # Override the exit status if it was a timeout.
197 if was_timeout:
198 exit_status = eTimedOut
199
200 # If we didn't end up with any output, call it empty for
201 # stdout/stderr.
202 if output is None:
203 output = ('', '')
204
205 # Now parse the output.
206 passes, failures, unexpected_successes = parse_test_results(output)
207 if exit_status == 0:
208 # stdout does not have any useful information from 'dotest.py',
209 # only stderr does.
210 report_test_pass(self.file_name, output[1])
211 else:
212 report_test_failure(self.file_name, command, output[1])
213
214 # Save off the results for the caller.
215 self.results = (
216 self.file_name,
217 exit_status,
218 passes,
219 failures,
220 unexpected_successes)
221
Todd Fiala51831472015-12-09 06:45:43 +0000222 def is_exceptional_exit(self):
223 """Returns whether the process returned a timeout.
224
225 Not valid to call until after on_process_exited() completes.
226
227 @return True if the exit is an exceptional exit (e.g. signal on
228 POSIX); False otherwise.
229 """
230 if self.results is None:
231 raise Exception(
232 "exit status checked before results are available")
233 return self.process_helper.is_exceptional_exit(
234 self.results[1])
235
236 def exceptional_exit_details(self):
237 if self.results is None:
238 raise Exception(
239 "exit status checked before results are available")
240 return self.process_helper.exceptional_exit_details(self.results[1])
241
242 def is_timeout(self):
243 if self.results is None:
244 raise Exception(
245 "exit status checked before results are available")
246 return self.results[1] == eTimedOut
247
Todd Fiala2d3754d2015-09-29 22:19:06 +0000248
249def get_soft_terminate_timeout():
250 # Defaults to 10 seconds, but can set
251 # LLDB_TEST_SOFT_TERMINATE_TIMEOUT to a floating point
252 # number in seconds. This value indicates how long
253 # the test runner will wait for the dotest inferior to
254 # handle a timeout via a soft terminate before it will
255 # assume that failed and do a hard terminate.
256
257 # TODO plumb through command-line option
258 return float(os.environ.get('LLDB_TEST_SOFT_TERMINATE_TIMEOUT', 10.0))
259
260
261def want_core_on_soft_terminate():
262 # TODO plumb through command-line option
263 if platform.system() == 'Linux':
264 return True
265 else:
266 return False
Todd Fialada817b62015-09-22 18:05:11 +0000267
268
Todd Fiala51831472015-12-09 06:45:43 +0000269def send_events_to_collector(events, command):
270 """Sends the given events to the collector described in the command line.
271
272 @param events the list of events to send to the test event collector.
273 @param command the inferior command line which contains the details on
274 how to connect to the test event collector.
275 """
276 if events is None or len(events) == 0:
277 # Nothing to do.
278 return
279
280 # Find the port we need to connect to from the --results-port option.
281 try:
282 arg_index = command.index("--results-port") + 1
283 except ValueError:
284 # There is no results port, so no way to communicate back to
285 # the event collector. This is not a problem if we're not
286 # using event aggregation.
287 # TODO flag as error once we always use the event system
288 print(
289 "INFO: no event collector, skipping post-inferior test "
290 "event reporting")
291 return
292
293 if arg_index >= len(command):
294 raise Exception(
295 "expected collector port at index {} in {}".format(
296 arg_index, command))
297 event_port = int(command[arg_index])
298
299 # Create results formatter connected back to collector via socket.
300 config = result_formatter.FormatterConfig()
301 config.port = event_port
302 formatter_spec = result_formatter.create_results_formatter(config)
303 if formatter_spec is None or formatter_spec.formatter is None:
304 raise Exception(
305 "Failed to create socket-based ResultsFormatter "
306 "back to test event collector")
307
308 # Send the events: the port-based event just pickles the content
309 # and sends over to the server side of the socket.
310 for event in events:
311 formatter_spec.formatter.handle_event(event)
312
313 # Cleanup
314 if formatter_spec.cleanup_func is not None:
315 formatter_spec.cleanup_func()
316
317
318def send_inferior_post_run_events(command, worker_index, process_driver):
319 """Sends any test events that should be generated after the inferior runs.
320
321 These events would include timeouts and exceptional (i.e. signal-returning)
322 process completion results.
323
324 @param command the list of command parameters passed to subprocess.Popen().
325 @param worker_index the worker index (possibly None) used to run
326 this process
327 @param process_driver the ProcessDriver-derived instance that was used
328 to run the inferior process.
329 """
330 if process_driver is None:
331 raise Exception("process_driver must not be None")
332 if process_driver.results is None:
333 # Invalid condition - the results should have been set one way or
334 # another, even in a timeout.
335 raise Exception("process_driver.results were not set")
336
337 # The code below fills in the post events struct. If there are any post
338 # events to fire up, we'll try to make a connection to the socket and
339 # provide the results.
340 post_events = []
341
342 # Handle signal/exceptional exits.
343 if process_driver.is_exceptional_exit():
344 (code, desc) = process_driver.exceptional_exit_details()
345 test_filename = process_driver.results[0]
346 post_events.append(
347 EventBuilder.event_for_job_exceptional_exit(
348 process_driver.pid,
349 worker_index,
350 code,
351 desc,
352 test_filename,
353 command))
354
355 # Handle timeouts.
356 if process_driver.is_timeout():
357 test_filename = process_driver.results[0]
358 post_events.append(EventBuilder.event_for_job_timeout(
359 process_driver.pid,
360 worker_index,
361 test_filename,
362 command))
363
364 if len(post_events) > 0:
365 send_events_to_collector(post_events, command)
366
367
Todd Fiala8cbeed32015-09-08 22:22:33 +0000368def call_with_timeout(command, timeout, name, inferior_pid_events):
Todd Fiala2d3754d2015-09-29 22:19:06 +0000369 # Add our worker index (if we have one) to all test events
370 # from this inferior.
Todd Fiala51831472015-12-09 06:45:43 +0000371 worker_index = None
Todd Fiala33896a92015-09-18 21:01:13 +0000372 if GET_WORKER_INDEX is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000373 try:
374 worker_index = GET_WORKER_INDEX()
375 command.extend([
Todd Fiala2d3754d2015-09-29 22:19:06 +0000376 "--event-add-entries",
377 "worker_index={}:int".format(worker_index)])
378 except: # pylint: disable=bare-except
379 # Ctrl-C does bad things to multiprocessing.Manager.dict()
380 # lookup. Just swallow it.
Todd Fialae83f1402015-09-18 22:45:31 +0000381 pass
382
Todd Fiala2d3754d2015-09-29 22:19:06 +0000383 # Create the inferior dotest.py ProcessDriver.
384 soft_terminate_timeout = get_soft_terminate_timeout()
385 want_core = want_core_on_soft_terminate()
Todd Fiala68615ce2015-09-15 21:38:04 +0000386
Todd Fiala2d3754d2015-09-29 22:19:06 +0000387 process_driver = DoTestProcessDriver(
388 sys.stdout,
389 output_lock,
390 inferior_pid_events,
391 name,
392 soft_terminate_timeout)
Todd Fiala68615ce2015-09-15 21:38:04 +0000393
Todd Fiala2d3754d2015-09-29 22:19:06 +0000394 # Run it with a timeout.
395 process_driver.run_command_with_timeout(command, timeout, want_core)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000396
Todd Fiala2d3754d2015-09-29 22:19:06 +0000397 # Return the results.
398 if not process_driver.results:
399 # This is truly exceptional. Even a failing or timed out
400 # binary should have called the results-generation code.
401 raise Exception("no test results were generated whatsoever")
Todd Fiala51831472015-12-09 06:45:43 +0000402
403 # Handle cases where the test inferior cannot adequately provide
404 # meaningful results to the test event system.
405 send_inferior_post_run_events(
406 command,
407 worker_index,
408 process_driver)
409
410
Todd Fiala2d3754d2015-09-29 22:19:06 +0000411 return process_driver.results
Johnny Chene8d9dc62011-10-31 19:04:07 +0000412
Chaoren Linb6325d02015-08-12 18:02:54 +0000413
Zachary Turner7d564542015-11-02 19:19:49 +0000414def process_dir(root, files, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000415 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000416 results = []
Steve Puccibefe2b12014-03-07 00:01:11 +0000417 for name in files:
Zachary Turner7d564542015-11-02 19:19:49 +0000418 import __main__ as main
419 script_file = main.__file__
Zachary Turnerf6896b02015-01-05 19:37:03 +0000420 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000421 dotest_argv +
Todd Fialafed95662015-09-03 18:58:44 +0000422 ["--inferior", "-p", name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000423
424 timeout_name = os.path.basename(os.path.splitext(name)[0]).upper()
425
Chaoren Linb6325d02015-08-12 18:02:54 +0000426 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
427 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000428
Todd Fiala8cbeed32015-09-08 22:22:33 +0000429 results.append(call_with_timeout(
430 command, timeout, name, inferior_pid_events))
Vince Harron17f429f2014-12-13 00:08:19 +0000431
Zachary Turner4cceca72015-08-14 16:45:32 +0000432 # result = (name, status, passes, failures, unexpected_successes)
433 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000434 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000435 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000436 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000437 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000438 if status != ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000439 unexpected_passes = [name for name, _, _, _, unexpected_successes in results
440 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000441
Chaoren Line80372a2015-08-12 18:02:53 +0000442 pass_count = sum([result[2] for result in results])
443 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000444
Zachary Turner4cceca72015-08-14 16:45:32 +0000445 return (timed_out, passed, failed, unexpected_passes, pass_count, fail_count)
Steve Puccibefe2b12014-03-07 00:01:11 +0000446
447in_q = None
448out_q = None
449
Chaoren Linb6325d02015-08-12 18:02:54 +0000450
Todd Fiala8cbeed32015-09-08 22:22:33 +0000451def process_dir_worker_multiprocessing(
452 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000453 a_dotest_options, job_queue, result_queue, inferior_pid_events,
454 worker_index_map):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000455 """Worker thread main loop when in multiprocessing mode.
Steve Puccibefe2b12014-03-07 00:01:11 +0000456 Takes one directory specification at a time and works on it."""
Todd Fiala8cbeed32015-09-08 22:22:33 +0000457
458 # Shut off interrupt handling in the child process.
459 signal.signal(signal.SIGINT, signal.SIG_IGN)
Ying Chend93aa102015-09-23 21:53:18 +0000460 if hasattr(signal, 'SIGHUP'):
461 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000462
463 # Setup the global state for the worker process.
464 setup_global_variables(
465 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000466 a_dotest_options, worker_index_map)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000467
468 # Keep grabbing entries from the queue until done.
469 while not job_queue.empty():
470 try:
471 job = job_queue.get(block=False)
Pavel Labath48c6b522015-11-02 20:54:25 +0000472 result = process_dir(job[0], job[1], job[2],
Todd Fiala8cbeed32015-09-08 22:22:33 +0000473 inferior_pid_events)
474 result_queue.put(result)
Zachary Turner814236d2015-10-21 17:48:52 +0000475 except queue.Empty:
Todd Fiala8cbeed32015-09-08 22:22:33 +0000476 # Fine, we're done.
477 pass
Steve Puccibefe2b12014-03-07 00:01:11 +0000478
Chaoren Linb6325d02015-08-12 18:02:54 +0000479
Todd Fiala8cbeed32015-09-08 22:22:33 +0000480def process_dir_worker_multiprocessing_pool(args):
481 return process_dir(*args)
482
483
Todd Fiala68615ce2015-09-15 21:38:04 +0000484def process_dir_worker_threading(job_queue, result_queue, inferior_pid_events):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000485 """Worker thread main loop when in threading mode.
486
487 This one supports the hand-rolled pooling support.
488
489 Takes one directory specification at a time and works on it."""
490
491 # Keep grabbing entries from the queue until done.
492 while not job_queue.empty():
493 try:
494 job = job_queue.get(block=False)
Pavel Labath48c6b522015-11-02 20:54:25 +0000495 result = process_dir(job[0], job[1], job[2],
Todd Fiala8cbeed32015-09-08 22:22:33 +0000496 inferior_pid_events)
497 result_queue.put(result)
Zachary Turner814236d2015-10-21 17:48:52 +0000498 except queue.Empty:
Todd Fiala8cbeed32015-09-08 22:22:33 +0000499 # Fine, we're done.
500 pass
501
502
503def process_dir_worker_threading_pool(args):
504 return process_dir(*args)
505
506
507def process_dir_mapper_inprocess(args):
508 """Map adapter for running the subprocess-based, non-threaded test runner.
509
510 @param args the process work item tuple
511 @return the test result tuple
512 """
513 return process_dir(*args)
514
515
516def collect_active_pids_from_pid_events(event_queue):
517 """
518 Returns the set of what should be active inferior pids based on
519 the event stream.
520
521 @param event_queue a multiprocessing.Queue containing events of the
522 form:
523 ('created', pid)
524 ('destroyed', pid)
525
526 @return set of inferior dotest.py pids activated but never completed.
527 """
528 active_pid_set = set()
529 while not event_queue.empty():
530 pid_event = event_queue.get_nowait()
531 if pid_event[0] == 'created':
532 active_pid_set.add(pid_event[1])
533 elif pid_event[0] == 'destroyed':
534 active_pid_set.remove(pid_event[1])
535 return active_pid_set
536
537
538def kill_all_worker_processes(workers, inferior_pid_events):
539 """
540 Kills all specified worker processes and their process tree.
541
542 @param workers a list of multiprocess.Process worker objects.
543 @param inferior_pid_events a multiprocess.Queue that contains
544 all inferior create and destroy events. Used to construct
545 the list of child pids still outstanding that need to be killed.
546 """
547 for worker in workers:
548 worker.terminate()
549 worker.join()
550
551 # Add all the child test pids created.
552 active_pid_set = collect_active_pids_from_pid_events(
553 inferior_pid_events)
554 for inferior_pid in active_pid_set:
Zachary Turnerff890da2015-10-19 23:45:41 +0000555 print("killing inferior pid {}".format(inferior_pid))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000556 os.kill(inferior_pid, signal.SIGKILL)
557
558
559def kill_all_worker_threads(workers, inferior_pid_events):
560 """
561 Kills all specified worker threads and their process tree.
562
563 @param workers a list of multiprocess.Process worker objects.
564 @param inferior_pid_events a multiprocess.Queue that contains
565 all inferior create and destroy events. Used to construct
566 the list of child pids still outstanding that need to be killed.
567 """
568
569 # Add all the child test pids created.
570 active_pid_set = collect_active_pids_from_pid_events(
571 inferior_pid_events)
572 for inferior_pid in active_pid_set:
Zachary Turnerff890da2015-10-19 23:45:41 +0000573 print("killing inferior pid {}".format(inferior_pid))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000574 os.kill(inferior_pid, signal.SIGKILL)
575
576 # We don't have a way to nuke the threads. However, since we killed
577 # all the inferiors, and we drained the job queue, this will be
578 # good enough. Wait cleanly for each worker thread to wrap up.
579 for worker in workers:
580 worker.join()
581
582
583def find_test_files_in_dir_tree(dir_root, found_func):
584 """Calls found_func for all the test files in the given dir hierarchy.
585
586 @param dir_root the path to the directory to start scanning
587 for test files. All files in this directory and all its children
588 directory trees will be searched.
589
590 @param found_func a callable object that will be passed
591 the parent directory (relative to dir_root) and the list of
592 test files from within that directory.
593 """
594 for root, _, files in os.walk(dir_root, topdown=False):
595 def is_test_filename(test_dir, base_filename):
596 """Returns True if the given filename matches the test name format.
597
598 @param test_dir the directory to check. Should be absolute or
599 relative to current working directory.
600
601 @param base_filename the base name of the filename to check for a
602 dherence to the python test case filename format.
603
604 @return True if name matches the python test case filename format.
605 """
606 # Not interested in symbolically linked files.
607 if os.path.islink(os.path.join(test_dir, base_filename)):
608 return False
609 # Only interested in test files with the "Test*.py" naming pattern.
610 return (base_filename.startswith("Test") and
611 base_filename.endswith(".py"))
612
613 tests = [filename for filename in files
614 if is_test_filename(root, filename)]
615 if tests:
616 found_func(root, tests)
617
618
619def initialize_global_vars_common(num_threads, test_work_items):
620 global total_tests, test_counter, test_name_len
Todd Fiala51831472015-12-09 06:45:43 +0000621
Todd Fiala8cbeed32015-09-08 22:22:33 +0000622 total_tests = sum([len(item[1]) for item in test_work_items])
623 test_counter = multiprocessing.Value('i', 0)
624 test_name_len = multiprocessing.Value('i', 0)
Greg Clayton1827fc22015-09-19 00:39:09 +0000625 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
Zachary Turnerff890da2015-10-19 23:45:41 +0000626 print("Testing: %d test suites, %d thread%s" % (
627 total_tests, num_threads, (num_threads > 1) * "s"), file=sys.stderr)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000628 update_progress()
629
630
631def initialize_global_vars_multiprocessing(num_threads, test_work_items):
632 # Initialize the global state we'll use to communicate with the
633 # rest of the flat module.
634 global output_lock
635 output_lock = multiprocessing.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000636
Todd Fiala8cbeed32015-09-08 22:22:33 +0000637 initialize_global_vars_common(num_threads, test_work_items)
638
639
640def initialize_global_vars_threading(num_threads, test_work_items):
Todd Fiala33896a92015-09-18 21:01:13 +0000641 """Initializes global variables used in threading mode.
642 @param num_threads specifies the number of workers used.
643 @param test_work_items specifies all the work items
644 that will be processed.
645 """
Todd Fiala8cbeed32015-09-08 22:22:33 +0000646 # Initialize the global state we'll use to communicate with the
647 # rest of the flat module.
648 global output_lock
649 output_lock = threading.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000650
651 index_lock = threading.RLock()
652 index_map = {}
653
654 def get_worker_index_threading():
655 """Returns a 0-based, thread-unique index for the worker thread."""
656 thread_id = threading.current_thread().ident
657 with index_lock:
658 if thread_id not in index_map:
659 index_map[thread_id] = len(index_map)
660 return index_map[thread_id]
661
662
663 global GET_WORKER_INDEX
664 GET_WORKER_INDEX = get_worker_index_threading
665
Todd Fiala8cbeed32015-09-08 22:22:33 +0000666 initialize_global_vars_common(num_threads, test_work_items)
667
668
Todd Fiala68615ce2015-09-15 21:38:04 +0000669def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler):
670 """Provides a main loop that is Ctrl-C protected.
671
672 The main loop calls the main_op_func() repeatedly until done_func()
673 returns true. The ctrl_c_handler() method is called with a single
674 int parameter that contains the number of times the ctrl_c has been
675 hit (starting with 1). The ctrl_c_handler() should mutate whatever
676 it needs to have the done_func() return True as soon as it is desired
677 to exit the loop.
678 """
679 done = False
680 ctrl_c_count = 0
681
682 while not done:
683 try:
684 # See if we're done. Start with done check since it is
685 # the first thing executed after a Ctrl-C handler in the
686 # following loop.
687 done = done_func()
688 if not done:
689 # Run the main op once.
690 main_op_func()
691
692 except KeyboardInterrupt:
693 ctrl_c_count += 1
694 ctrl_c_handler(ctrl_c_count)
695
696
697def pump_workers_and_asyncore_map(workers, asyncore_map):
698 """Prunes out completed workers and maintains the asyncore loop.
699
700 The asyncore loop contains the optional socket listener
701 and handlers. When all workers are complete, this method
702 takes care of stopping the listener. It also runs the
703 asyncore loop for the given async map for 10 iterations.
704
705 @param workers the list of worker Thread/Process instances.
706
707 @param asyncore_map the asyncore threading-aware map that
708 indicates which channels are in use and still alive.
709 """
710
711 # Check on all the workers, removing them from the workers
712 # list as they complete.
713 dead_workers = []
714 for worker in workers:
715 # This non-blocking join call is what allows us
716 # to still receive keyboard interrupts.
717 worker.join(0.01)
718 if not worker.is_alive():
719 dead_workers.append(worker)
720 # Clear out the completed workers
721 for dead_worker in dead_workers:
722 workers.remove(dead_worker)
723
724 # If there are no more workers and there is a listener,
725 # close the listener.
726 global RESULTS_LISTENER_CHANNEL
727 if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None:
728 RESULTS_LISTENER_CHANNEL.close()
729 RESULTS_LISTENER_CHANNEL = None
730
731 # Pump the asyncore map if it isn't empty.
732 if len(asyncore_map) > 0:
733 asyncore.loop(0.1, False, asyncore_map, 10)
734
735
736def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events,
737 stop_all_inferiors_func):
738 """Performs the appropriate ctrl-c action for non-pool parallel test runners
739
740 @param ctrl_c_count starting with 1, indicates the number of times ctrl-c
741 has been intercepted. The value is 1 on the first intercept, 2 on the
742 second, etc.
743
744 @param job_queue a Queue object that contains the work still outstanding
745 (i.e. hasn't been assigned to a worker yet).
746
747 @param workers list of Thread or Process workers.
748
749 @param inferior_pid_events specifies a Queue of inferior process
750 construction and destruction events. Used to build the list of inferior
751 processes that should be killed if we get that far.
752
753 @param stop_all_inferiors_func a callable object that takes the
754 workers and inferior_pid_events parameters (in that order) if a hard
755 stop is to be used on the workers.
756 """
757
758 # Print out which Ctrl-C we're handling.
759 key_name = [
760 "first",
761 "second",
762 "third",
763 "many"]
764
765 if ctrl_c_count < len(key_name):
766 name_index = ctrl_c_count - 1
767 else:
768 name_index = len(key_name) - 1
769 message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index])
770 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000771 print(message)
Todd Fiala68615ce2015-09-15 21:38:04 +0000772
773 if ctrl_c_count == 1:
774 # Remove all outstanding items from the work queue so we stop
775 # doing any more new work.
776 while not job_queue.empty():
777 try:
778 # Just drain it to stop more work from being started.
779 job_queue.get_nowait()
Zachary Turner814236d2015-10-21 17:48:52 +0000780 except queue.Empty:
Todd Fiala68615ce2015-09-15 21:38:04 +0000781 pass
782 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000783 print("Stopped more work from being started.")
Todd Fiala68615ce2015-09-15 21:38:04 +0000784 elif ctrl_c_count == 2:
785 # Try to stop all inferiors, even the ones currently doing work.
786 stop_all_inferiors_func(workers, inferior_pid_events)
787 else:
788 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000789 print("All teardown activities kicked off, should finish soon.")
Todd Fiala68615ce2015-09-15 21:38:04 +0000790
791
792def workers_and_async_done(workers, async_map):
793 """Returns True if the workers list and asyncore channels are all done.
794
795 @param workers list of workers (threads/processes). These must adhere
796 to the threading Thread or multiprocessing.Process interface.
797
798 @param async_map the threading-aware asyncore channel map to check
799 for live channels.
800
801 @return False if the workers list exists and has any entries in it, or
802 if the async_map exists and has any entries left in it; otherwise, True.
803 """
804 if workers is not None and len(workers) > 0:
805 # We're not done if we still have workers left.
806 return False
807 if async_map is not None and len(async_map) > 0:
808 return False
809 # We're done.
810 return True
811
812
Todd Fiala8cbeed32015-09-08 22:22:33 +0000813def multiprocessing_test_runner(num_threads, test_work_items):
814 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
815
816 This concurrent test runner is based on the multiprocessing
817 library, and rolls its own worker pooling strategy so it
818 can handle Ctrl-C properly.
819
820 This test runner is known to have an issue running on
821 Windows platforms.
822
823 @param num_threads the number of worker processes to use.
824
825 @param test_work_items the iterable of test work item tuples
826 to run.
827 """
828
829 # Initialize our global state.
830 initialize_global_vars_multiprocessing(num_threads, test_work_items)
831
832 # Create jobs.
833 job_queue = multiprocessing.Queue(len(test_work_items))
834 for test_work_item in test_work_items:
835 job_queue.put(test_work_item)
836
837 result_queue = multiprocessing.Queue(len(test_work_items))
838
839 # Create queues for started child pids. Terminating
840 # the multiprocess processes does not terminate the
841 # child processes they spawn. We can remove this tracking
842 # if/when we move to having the multiprocess process directly
843 # perform the test logic. The Queue size needs to be able to
844 # hold 2 * (num inferior dotest.py processes started) entries.
845 inferior_pid_events = multiprocessing.Queue(4096)
846
Todd Fiala33896a92015-09-18 21:01:13 +0000847 # Worker dictionary allows each worker to figure out its worker index.
848 manager = multiprocessing.Manager()
849 worker_index_map = manager.dict()
850
Todd Fiala8cbeed32015-09-08 22:22:33 +0000851 # Create workers. We don't use multiprocessing.Pool due to
852 # challenges with handling ^C keyboard interrupts.
853 workers = []
854 for _ in range(num_threads):
855 worker = multiprocessing.Process(
856 target=process_dir_worker_multiprocessing,
857 args=(output_lock,
858 test_counter,
859 total_tests,
860 test_name_len,
861 dotest_options,
862 job_queue,
863 result_queue,
Todd Fiala33896a92015-09-18 21:01:13 +0000864 inferior_pid_events,
865 worker_index_map))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000866 worker.start()
867 workers.append(worker)
868
Todd Fiala68615ce2015-09-15 21:38:04 +0000869 # Main loop: wait for all workers to finish and wait for
870 # the socket handlers to wrap up.
871 ctrl_c_loop(
872 # Main operation of loop
873 lambda: pump_workers_and_asyncore_map(
874 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000875
Todd Fiala68615ce2015-09-15 21:38:04 +0000876 # Return True when we're done with the main loop.
877 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000878
Todd Fiala68615ce2015-09-15 21:38:04 +0000879 # Indicate what we do when we receive one or more Ctrl-Cs.
880 lambda ctrl_c_count: handle_ctrl_c(
881 ctrl_c_count, job_queue, workers, inferior_pid_events,
882 kill_all_worker_processes))
883
884 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000885 test_results = []
886 while not result_queue.empty():
887 test_results.append(result_queue.get(block=False))
888 return test_results
889
890
Todd Fiala68615ce2015-09-15 21:38:04 +0000891def map_async_run_loop(future, channel_map, listener_channel):
892 """Blocks until the Pool.map_async completes and the channel completes.
893
894 @param future an AsyncResult instance from a Pool.map_async() call.
895
896 @param channel_map the asyncore dispatch channel map that should be pumped.
897 Optional: may be None.
898
899 @param listener_channel the channel representing a listener that should be
900 closed once the map_async results are available.
901
902 @return the results from the async_result instance.
903 """
904 map_results = None
905
906 done = False
907 while not done:
908 # Check if we need to reap the map results.
909 if map_results is None:
910 if future.ready():
911 # Get the results.
912 map_results = future.get()
913
914 # Close the runner process listener channel if we have
915 # one: no more connections will be incoming.
916 if listener_channel is not None:
917 listener_channel.close()
918
919 # Pump the asyncore loop if we have a listener socket.
920 if channel_map is not None:
921 asyncore.loop(0.01, False, channel_map, 10)
922
923 # Figure out if we're done running.
924 done = map_results is not None
925 if channel_map is not None:
926 # We have a runner process async map. Check if it
927 # is complete.
928 if len(channel_map) > 0:
929 # We still have an asyncore channel running. Not done yet.
930 done = False
931
932 return map_results
933
934
Todd Fiala8cbeed32015-09-08 22:22:33 +0000935def multiprocessing_test_runner_pool(num_threads, test_work_items):
936 # Initialize our global state.
937 initialize_global_vars_multiprocessing(num_threads, test_work_items)
938
Todd Fiala33896a92015-09-18 21:01:13 +0000939 manager = multiprocessing.Manager()
940 worker_index_map = manager.dict()
941
Todd Fiala8cbeed32015-09-08 22:22:33 +0000942 pool = multiprocessing.Pool(
943 num_threads,
944 initializer=setup_global_variables,
945 initargs=(output_lock, test_counter, total_tests, test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000946 dotest_options, worker_index_map))
Todd Fiala68615ce2015-09-15 21:38:04 +0000947
948 # Start the map operation (async mode).
949 map_future = pool.map_async(
950 process_dir_worker_multiprocessing_pool, test_work_items)
951 return map_async_run_loop(
952 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000953
954
955def threading_test_runner(num_threads, test_work_items):
956 """Provides hand-wrapped pooling threading-based test runner adapter
957 with Ctrl-C support.
958
959 This concurrent test runner is based on the threading
960 library, and rolls its own worker pooling strategy so it
961 can handle Ctrl-C properly.
962
963 @param num_threads the number of worker processes to use.
964
965 @param test_work_items the iterable of test work item tuples
966 to run.
967 """
968
969 # Initialize our global state.
970 initialize_global_vars_threading(num_threads, test_work_items)
971
972 # Create jobs.
Zachary Turner814236d2015-10-21 17:48:52 +0000973 job_queue = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000974 for test_work_item in test_work_items:
975 job_queue.put(test_work_item)
976
Zachary Turner814236d2015-10-21 17:48:52 +0000977 result_queue = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000978
979 # Create queues for started child pids. Terminating
980 # the threading threads does not terminate the
981 # child processes they spawn.
Zachary Turner814236d2015-10-21 17:48:52 +0000982 inferior_pid_events = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000983
984 # Create workers. We don't use multiprocessing.pool.ThreadedPool
985 # due to challenges with handling ^C keyboard interrupts.
986 workers = []
987 for _ in range(num_threads):
988 worker = threading.Thread(
989 target=process_dir_worker_threading,
Todd Fiala68615ce2015-09-15 21:38:04 +0000990 args=(job_queue,
Todd Fiala8cbeed32015-09-08 22:22:33 +0000991 result_queue,
992 inferior_pid_events))
993 worker.start()
994 workers.append(worker)
995
Todd Fiala68615ce2015-09-15 21:38:04 +0000996 # Main loop: wait for all workers to finish and wait for
997 # the socket handlers to wrap up.
998 ctrl_c_loop(
999 # Main operation of loop
1000 lambda: pump_workers_and_asyncore_map(
1001 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +00001002
Todd Fiala68615ce2015-09-15 21:38:04 +00001003 # Return True when we're done with the main loop.
1004 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +00001005
Todd Fiala68615ce2015-09-15 21:38:04 +00001006 # Indicate what we do when we receive one or more Ctrl-Cs.
1007 lambda ctrl_c_count: handle_ctrl_c(
1008 ctrl_c_count, job_queue, workers, inferior_pid_events,
1009 kill_all_worker_threads))
Todd Fiala8cbeed32015-09-08 22:22:33 +00001010
Todd Fiala68615ce2015-09-15 21:38:04 +00001011 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001012 test_results = []
1013 while not result_queue.empty():
1014 test_results.append(result_queue.get(block=False))
1015 return test_results
1016
1017
1018def threading_test_runner_pool(num_threads, test_work_items):
1019 # Initialize our global state.
1020 initialize_global_vars_threading(num_threads, test_work_items)
1021
Todd Fiala68615ce2015-09-15 21:38:04 +00001022 pool = multiprocessing.pool.ThreadPool(num_threads)
1023 map_future = pool.map_async(
1024 process_dir_worker_threading_pool, test_work_items)
1025
1026 return map_async_run_loop(
1027 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
1028
1029
1030def asyncore_run_loop(channel_map):
1031 try:
1032 asyncore.loop(None, False, channel_map)
1033 except:
1034 # Swallow it, we're seeing:
1035 # error: (9, 'Bad file descriptor')
1036 # when the listener channel is closed. Shouldn't be the case.
1037 pass
Todd Fiala8cbeed32015-09-08 22:22:33 +00001038
1039
1040def inprocess_exec_test_runner(test_work_items):
1041 # Initialize our global state.
1042 initialize_global_vars_multiprocessing(1, test_work_items)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001043
Todd Fiala33896a92015-09-18 21:01:13 +00001044 # We're always worker index 0
1045 global GET_WORKER_INDEX
1046 GET_WORKER_INDEX = lambda: 0
1047
Todd Fiala68615ce2015-09-15 21:38:04 +00001048 # Run the listener and related channel maps in a separate thread.
1049 # global RUNNER_PROCESS_ASYNC_MAP
1050 global RESULTS_LISTENER_CHANNEL
1051 if RESULTS_LISTENER_CHANNEL is not None:
1052 socket_thread = threading.Thread(
1053 target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP))
1054 socket_thread.start()
1055
1056 # Do the work.
Zachary Turner1c4059a2015-10-22 20:39:59 +00001057 test_results = list(map(process_dir_mapper_inprocess, test_work_items))
Todd Fiala68615ce2015-09-15 21:38:04 +00001058
1059 # If we have a listener channel, shut it down here.
1060 if RESULTS_LISTENER_CHANNEL is not None:
1061 # Close down the channel.
1062 RESULTS_LISTENER_CHANNEL.close()
1063 RESULTS_LISTENER_CHANNEL = None
1064
1065 # Wait for the listener and handlers to complete.
1066 socket_thread.join()
1067
1068 return test_results
Todd Fiala8cbeed32015-09-08 22:22:33 +00001069
Todd Fialad06a9c92015-12-12 00:34:57 +00001070def walk_and_invoke(test_files, dotest_argv, num_workers, test_runner_func):
1071 """Invokes the test runner on each test file specified by test_files.
Vince Harrone06a7a82015-05-12 23:12:19 +00001072
Todd Fialad06a9c92015-12-12 00:34:57 +00001073 @param test_files a list of (test_subdir, list_of_test_files_in_dir)
1074 @param num_workers the number of worker queues working on these test files
1075 @param test_runner_func the test runner configured to run the tests
1076
1077 @return a tuple of results from the running of the specified tests,
1078 of the form (timed_out, passed, failed, unexpected_successes, pass_count,
1079 fail_count)
Vince Harrone06a7a82015-05-12 23:12:19 +00001080 """
Todd Fiala68615ce2015-09-15 21:38:04 +00001081 # The async_map is important to keep all thread-related asyncore
1082 # channels distinct when we call asyncore.loop() later on.
1083 global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP
1084 RUNNER_PROCESS_ASYNC_MAP = {}
1085
1086 # If we're outputting side-channel test results, create the socket
1087 # listener channel and tell the inferior to send results to the
1088 # port on which we'll be listening.
1089 if RESULTS_FORMATTER is not None:
Todd Fialae83f1402015-09-18 22:45:31 +00001090 forwarding_func = RESULTS_FORMATTER.handle_event
Todd Fiala68615ce2015-09-15 21:38:04 +00001091 RESULTS_LISTENER_CHANNEL = (
1092 dotest_channels.UnpicklingForwardingListenerChannel(
Todd Fiala871b2e52015-09-22 22:47:34 +00001093 RUNNER_PROCESS_ASYNC_MAP, "localhost", 0,
1094 2 * num_workers, forwarding_func))
Todd Fiala68615ce2015-09-15 21:38:04 +00001095 dotest_argv.append("--results-port")
1096 dotest_argv.append(str(RESULTS_LISTENER_CHANNEL.address[1]))
Todd Fiala3f0a3602014-07-08 06:42:37 +00001097
Todd Fialad06a9c92015-12-12 00:34:57 +00001098 # Build the test work items out of the (dir, file_list) entries passed in.
Todd Fiala3f0a3602014-07-08 06:42:37 +00001099 test_work_items = []
Todd Fialad06a9c92015-12-12 00:34:57 +00001100 for entry in test_files:
1101 test_work_items.append((entry[0], entry[1], dotest_argv, None))
Todd Fiala3f0a3602014-07-08 06:42:37 +00001102
Todd Fiala8cbeed32015-09-08 22:22:33 +00001103 # Convert test work items into test results using whatever
1104 # was provided as the test run function.
1105 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +00001106
Todd Fiala8cbeed32015-09-08 22:22:33 +00001107 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +00001108 timed_out = sum([result[0] for result in test_results], [])
1109 passed = sum([result[1] for result in test_results], [])
1110 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +00001111 unexpected_successes = sum([result[3] for result in test_results], [])
1112 pass_count = sum([result[4] for result in test_results])
1113 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +00001114
Todd Fiala8cbeed32015-09-08 22:22:33 +00001115 return (timed_out, passed, failed, unexpected_successes, pass_count,
1116 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001117
Chaoren Linb6325d02015-08-12 18:02:54 +00001118
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001119def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +00001120 # returns a set of test filenames that might timeout
1121 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +00001122 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001123 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +00001124 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001125 else:
Todd Fiala68615ce2015-09-15 21:38:04 +00001126 m = re.search(r'remote-(\w+)', platform_name)
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001127 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +00001128
1129 expected_timeout = set()
Todd Fialaa8fee7f2015-12-11 19:44:23 +00001130 expected_timeout.add("TestExpectedTimeout.py")
Vince Harron06381732015-05-12 23:10:36 +00001131
1132 if target.startswith("linux"):
1133 expected_timeout |= {
Vince Harron06381732015-05-12 23:10:36 +00001134 "TestConnectRemote.py",
1135 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +00001136 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +00001137 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001138
1139 # Times out in ~10% of the times on the build bot
1140 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +00001141 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001142 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +00001143 "TestThreadStepOut.py",
1144 }
1145 elif target.startswith("android"):
1146 expected_timeout |= {
1147 "TestExitDuringStep.py",
1148 "TestHelloWorld.py",
1149 }
Chaoren Linfebef1b2015-08-19 17:22:12 +00001150 if host.startswith("win32"):
1151 expected_timeout |= {
1152 "TestEvents.py",
1153 "TestThreadStates.py",
1154 }
Ed Maste4dd8fba2015-05-14 16:25:52 +00001155 elif target.startswith("freebsd"):
1156 expected_timeout |= {
1157 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +00001158 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +00001159 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +00001160 "TestWatchpointConditionAPI.py",
1161 }
Vince Harron0f173ac2015-05-18 19:36:33 +00001162 elif target.startswith("darwin"):
1163 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +00001164 # times out on MBP Retina, Mid 2012
1165 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +00001166 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +00001167 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +00001168 }
Vince Harron06381732015-05-12 23:10:36 +00001169 return expected_timeout
1170
Chaoren Linb6325d02015-08-12 18:02:54 +00001171
Pavel Labathfad30cf2015-06-29 14:16:51 +00001172def getDefaultTimeout(platform_name):
1173 if os.getenv("LLDB_TEST_TIMEOUT"):
1174 return os.getenv("LLDB_TEST_TIMEOUT")
1175
1176 if platform_name is None:
1177 platform_name = sys.platform
1178
1179 if platform_name.startswith("remote-"):
1180 return "10m"
Todd Fiala88722f72015-11-11 05:10:07 +00001181 elif platform_name == 'darwin':
1182 # We are consistently needing more time on a few tests.
1183 return "6m"
Pavel Labathfad30cf2015-06-29 14:16:51 +00001184 else:
1185 return "4m"
1186
Chaoren Linb6325d02015-08-12 18:02:54 +00001187
Vince Harron0b9dbb52015-05-21 18:18:52 +00001188def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +00001189 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +00001190 os.utime(fname, times)
1191
Chaoren Linb6325d02015-08-12 18:02:54 +00001192
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001193def find(pattern, path):
1194 result = []
1195 for root, dirs, files in os.walk(path):
1196 for name in files:
1197 if fnmatch.fnmatch(name, pattern):
1198 result.append(os.path.join(root, name))
1199 return result
1200
Chaoren Linb6325d02015-08-12 18:02:54 +00001201
Todd Fiala8cbeed32015-09-08 22:22:33 +00001202def get_test_runner_strategies(num_threads):
1203 """Returns the test runner strategies by name in a dictionary.
1204
1205 @param num_threads specifies the number of threads/processes
1206 that will be used for concurrent test runners.
1207
1208 @return dictionary with key as test runner strategy name and
1209 value set to a callable object that takes the test work item
1210 and returns a test result tuple.
1211 """
1212 return {
1213 # multiprocessing supports ctrl-c and does not use
1214 # multiprocessing.Pool.
1215 "multiprocessing":
1216 (lambda work_items: multiprocessing_test_runner(
1217 num_threads, work_items)),
1218
1219 # multiprocessing-pool uses multiprocessing.Pool but
1220 # does not support Ctrl-C.
1221 "multiprocessing-pool":
1222 (lambda work_items: multiprocessing_test_runner_pool(
1223 num_threads, work_items)),
1224
1225 # threading uses a hand-rolled worker pool much
1226 # like multiprocessing, but instead uses in-process
1227 # worker threads. This one supports Ctrl-C.
1228 "threading":
1229 (lambda work_items: threading_test_runner(num_threads, work_items)),
1230
1231 # threading-pool uses threading for the workers (in-process)
1232 # and uses the multiprocessing.pool thread-enabled pool.
Todd Fiala68615ce2015-09-15 21:38:04 +00001233 # This does not properly support Ctrl-C.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001234 "threading-pool":
1235 (lambda work_items: threading_test_runner_pool(
1236 num_threads, work_items)),
1237
1238 # serial uses the subprocess-based, single process
1239 # test runner. This provides process isolation but
Todd Fiala68615ce2015-09-15 21:38:04 +00001240 # no concurrent test execution.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001241 "serial":
1242 inprocess_exec_test_runner
1243 }
1244
1245
Todd Fialaea736242015-09-23 15:21:28 +00001246def _remove_option(
1247 args, long_option_name, short_option_name, takes_arg):
Todd Fiala68615ce2015-09-15 21:38:04 +00001248 """Removes option and related option arguments from args array.
Todd Fialaea736242015-09-23 15:21:28 +00001249
1250 This method removes all short/long options that match the given
1251 arguments.
1252
Todd Fiala68615ce2015-09-15 21:38:04 +00001253 @param args the array of command line arguments (in/out)
Todd Fialaea736242015-09-23 15:21:28 +00001254
1255 @param long_option_name the full command line representation of the
1256 long-form option that will be removed (including '--').
1257
1258 @param short_option_name the short version of the command line option
1259 that will be removed (including '-').
1260
1261 @param takes_arg True if the option takes an argument.
1262
Todd Fiala68615ce2015-09-15 21:38:04 +00001263 """
Todd Fialaea736242015-09-23 15:21:28 +00001264 if long_option_name is not None:
1265 regex_string = "^" + long_option_name + "="
1266 long_regex = re.compile(regex_string)
1267 if short_option_name is not None:
1268 # Short options we only match the -X and assume
1269 # any arg is one command line argument jammed together.
1270 # i.e. -O--abc=1 is a single argument in the args list.
1271 # We don't handle -O --abc=1, as argparse doesn't handle
1272 # it, either.
1273 regex_string = "^" + short_option_name
1274 short_regex = re.compile(regex_string)
1275
1276 def remove_long_internal():
1277 """Removes one matching long option from args.
1278 @returns True if one was found and removed; False otherwise.
1279 """
1280 try:
1281 index = args.index(long_option_name)
1282 # Handle the exact match case.
1283 if takes_arg:
1284 removal_count = 2
1285 else:
1286 removal_count = 1
1287 del args[index:index+removal_count]
1288 return True
1289 except ValueError:
1290 # Thanks to argparse not handling options with known arguments
1291 # like other options parsing libraries (see
1292 # https://bugs.python.org/issue9334), we need to support the
1293 # --results-formatter-options={second-level-arguments} (note
1294 # the equal sign to fool the first-level arguments parser into
1295 # not treating the second-level arguments as first-level
1296 # options). We're certainly at risk of getting this wrong
1297 # since now we're forced into the business of trying to figure
1298 # out what is an argument (although I think this
1299 # implementation will suffice).
1300 for index in range(len(args)):
1301 match = long_regex.search(args[index])
1302 if match:
1303 del args[index]
1304 return True
1305 return False
1306
1307 def remove_short_internal():
1308 """Removes one matching short option from args.
1309 @returns True if one was found and removed; False otherwise.
1310 """
Todd Fiala68615ce2015-09-15 21:38:04 +00001311 for index in range(len(args)):
Todd Fialaea736242015-09-23 15:21:28 +00001312 match = short_regex.search(args[index])
Todd Fiala68615ce2015-09-15 21:38:04 +00001313 if match:
Todd Fiala68615ce2015-09-15 21:38:04 +00001314 del args[index]
Todd Fialaea736242015-09-23 15:21:28 +00001315 return True
1316 return False
Todd Fiala68615ce2015-09-15 21:38:04 +00001317
Todd Fialaea736242015-09-23 15:21:28 +00001318 removal_count = 0
1319 while long_option_name is not None and remove_long_internal():
1320 removal_count += 1
1321 while short_option_name is not None and remove_short_internal():
1322 removal_count += 1
1323 if removal_count == 0:
1324 raise Exception(
1325 "failed to find at least one of '{}', '{}' in options".format(
1326 long_option_name, short_option_name))
Todd Fiala68615ce2015-09-15 21:38:04 +00001327
1328
1329def adjust_inferior_options(dotest_argv):
1330 """Adjusts the commandline args array for inferiors.
1331
1332 This method adjusts the inferior dotest commandline options based
1333 on the parallel test runner's options. Some of the inferior options
1334 will need to change to properly handle aggregation functionality.
1335 """
1336 global dotest_options
1337
1338 # If we don't have a session directory, create one.
1339 if not dotest_options.s:
1340 # no session log directory, we need to add this to prevent
1341 # every dotest invocation from creating its own directory
1342 import datetime
1343 # The windows platforms don't like ':' in the pathname.
Zachary Turneraf383ff2015-10-27 22:33:47 +00001344 timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S")
Todd Fiala68615ce2015-09-15 21:38:04 +00001345 dotest_argv.append('-s')
1346 dotest_argv.append(timestamp_started)
1347 dotest_options.s = timestamp_started
1348
1349 # Adjust inferior results formatter options - if the parallel
1350 # test runner is collecting into the user-specified test results,
1351 # we'll have inferiors spawn with the --results-port option and
1352 # strip the original test runner options.
1353 if dotest_options.results_file is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001354 _remove_option(dotest_argv, "--results-file", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001355 if dotest_options.results_port is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001356 _remove_option(dotest_argv, "--results-port", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001357 if dotest_options.results_formatter is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001358 _remove_option(dotest_argv, "--results-formatter", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001359 if dotest_options.results_formatter_options is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001360 _remove_option(dotest_argv, "--results-formatter-option", "-O",
1361 True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001362
Todd Fialacee6a6a2015-11-09 18:51:04 +00001363 # Remove the --curses shortcut if specified.
1364 if dotest_options.curses:
1365 _remove_option(dotest_argv, "--curses", None, False)
1366
Todd Fiala33896a92015-09-18 21:01:13 +00001367 # Remove test runner name if present.
1368 if dotest_options.test_runner_name is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001369 _remove_option(dotest_argv, "--test-runner-name", None, True)
Todd Fiala33896a92015-09-18 21:01:13 +00001370
1371
Todd Fiala83c32e32015-09-22 21:19:40 +00001372def is_darwin_version_lower_than(target_version):
1373 """Checks that os is Darwin and version is lower than target_version.
1374
1375 @param target_version the StrictVersion indicating the version
1376 we're checking against.
1377
1378 @return True if the OS is Darwin (OS X) and the version number of
1379 the OS is less than target_version; False in all other cases.
1380 """
1381 if platform.system() != 'Darwin':
1382 # Can't be Darwin lower than a certain version.
1383 return False
1384
1385 system_version = distutils.version.StrictVersion(platform.mac_ver()[0])
Zachary Turnerbac6e4f2015-11-03 21:37:27 +00001386 return seven.cmp_(system_version, target_version) < 0
Todd Fiala83c32e32015-09-22 21:19:40 +00001387
1388
1389def default_test_runner_name(num_threads):
1390 """Returns the default test runner name for the configuration.
1391
1392 @param num_threads the number of threads/workers this test runner is
1393 supposed to use.
1394
1395 @return the test runner name that should be used by default when
1396 no test runner was explicitly called out on the command line.
1397 """
1398 if num_threads == 1:
1399 # Use the serial runner.
1400 test_runner_name = "serial"
1401 elif os.name == "nt":
Adrian McCarthy040b31d2015-10-12 14:46:57 +00001402 # On Windows, Python uses CRT with a low limit on the number of open
1403 # files. If you have a lot of cores, the threading-pool runner will
Zachary Turnerf0c3f682015-11-06 18:14:31 +00001404 # often fail because it exceeds that limit. It's not clear what the
1405 # right balance is, so until we can investigate it more deeply,
1406 # just use the one that works
1407 test_runner_name = "multiprocessing-pool"
Todd Fiala83c32e32015-09-22 21:19:40 +00001408 elif is_darwin_version_lower_than(
1409 distutils.version.StrictVersion("10.10.0")):
1410 # OS X versions before 10.10 appear to have an issue using
1411 # the threading test runner. Fall back to multiprocessing.
1412 # Supports Ctrl-C.
1413 test_runner_name = "multiprocessing"
1414 else:
1415 # For everyone else, use the ctrl-c-enabled threading support.
1416 # Should use fewer system resources than the multprocessing
1417 # variant.
1418 test_runner_name = "threading"
1419 return test_runner_name
1420
1421
Zachary Turner80310c22015-12-10 18:51:02 +00001422def main(num_threads, test_subdir, test_runner_name, results_formatter):
Todd Fialafed95662015-09-03 18:58:44 +00001423 """Run dotest.py in inferior mode in parallel.
1424
Todd Fialafed95662015-09-03 18:58:44 +00001425 @param num_threads the parsed value of the num-threads command line
1426 argument.
1427
1428 @param test_subdir optionally specifies a subdir to limit testing
1429 within. May be None if the entire test tree is to be used. This subdir
1430 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001431
1432 @param test_runner_name if specified, contains the test runner
1433 name which selects the strategy used to run the isolated and
1434 optionally concurrent test runner. Specify None to allow the
1435 system to choose the most appropriate test runner given desired
1436 thread count and OS type.
1437
Todd Fiala68615ce2015-09-15 21:38:04 +00001438 @param results_formatter if specified, provides the TestResultsFormatter
1439 instance that will format and output test result data from the
1440 side-channel test results. When specified, inferior dotest calls
1441 will send test results side-channel data over a socket to the parallel
1442 test runner, which will forward them on to results_formatter.
Todd Fialafed95662015-09-03 18:58:44 +00001443 """
1444
Todd Fiala1cc97b42015-09-21 05:42:26 +00001445 # Do not shut down on sighup.
Ying Chend93aa102015-09-23 21:53:18 +00001446 if hasattr(signal, 'SIGHUP'):
1447 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala1cc97b42015-09-21 05:42:26 +00001448
Todd Fialafed95662015-09-03 18:58:44 +00001449 dotest_argv = sys.argv[1:]
1450
Zachary Turner80310c22015-12-10 18:51:02 +00001451 global RESULTS_FORMATTER
Todd Fiala68615ce2015-09-15 21:38:04 +00001452 RESULTS_FORMATTER = results_formatter
Todd Fialafed95662015-09-03 18:58:44 +00001453
Vince Harrond5fa1022015-05-10 15:24:12 +00001454 # We can't use sys.path[0] to determine the script directory
1455 # because it doesn't work under a debugger
Vince Harron8994fed2015-05-22 19:49:23 +00001456 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +00001457 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +00001458 dotest_options = dotest_args.parse_args(parser, dotest_argv)
1459
Todd Fiala68615ce2015-09-15 21:38:04 +00001460 adjust_inferior_options(dotest_argv)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001461
1462 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +00001463
Vince Harrone06a7a82015-05-12 23:12:19 +00001464 # The root directory was specified on the command line
Todd Fiala68615ce2015-09-15 21:38:04 +00001465 test_directory = os.path.dirname(os.path.realpath(__file__))
Todd Fialafed95662015-09-03 18:58:44 +00001466 if test_subdir and len(test_subdir) > 0:
1467 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +00001468 else:
Todd Fialafed95662015-09-03 18:58:44 +00001469 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +00001470
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001471 # clean core files in test tree from previous runs (Linux)
1472 cores = find('core.*', test_subdir)
1473 for core in cores:
1474 os.unlink(core)
1475
Daniel Maleab42556f2013-04-19 18:32:53 +00001476 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +00001477
Todd Fialaa8fee7f2015-12-11 19:44:23 +00001478 # Figure out which test files should be enabled for expected
1479 # timeout
1480 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
1481 if results_formatter is not None:
1482 results_formatter.set_expected_timeouts_by_basename(expected_timeout)
1483
Todd Fiala8cbeed32015-09-08 22:22:33 +00001484 # Figure out which testrunner strategy we'll use.
1485 runner_strategies_by_name = get_test_runner_strategies(num_threads)
1486
1487 # If the user didn't specify a test runner strategy, determine
1488 # the default now based on number of threads and OS type.
1489 if not test_runner_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001490 test_runner_name = default_test_runner_name(num_threads)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001491
1492 if test_runner_name not in runner_strategies_by_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001493 raise Exception(
1494 "specified testrunner name '{}' unknown. Valid choices: {}".format(
1495 test_runner_name,
Zachary Turner606e1e32015-10-23 17:53:51 +00001496 list(runner_strategies_by_name.keys())))
Todd Fiala8cbeed32015-09-08 22:22:33 +00001497 test_runner_func = runner_strategies_by_name[test_runner_name]
1498
Todd Fialad06a9c92015-12-12 00:34:57 +00001499 test_files = []
1500 find_test_files_in_dir_tree(
1501 test_subdir, lambda tdir, tfiles: test_files.append(
1502 (test_subdir, tfiles)))
1503
Todd Fiala8cbeed32015-09-08 22:22:33 +00001504 summary_results = walk_and_invoke(
Todd Fialad06a9c92015-12-12 00:34:57 +00001505 test_files,
1506 dotest_argv,
1507 num_threads,
1508 test_runner_func)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001509
1510 (timed_out, passed, failed, unexpected_successes, pass_count,
1511 fail_count) = summary_results
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +00001512
Todd Fialade9a44e2015-09-22 00:15:50 +00001513 # The results formatter - if present - is done now. Tell it to
1514 # terminate.
1515 if results_formatter is not None:
1516 results_formatter.send_terminate_as_needed()
1517
Vince Harron17f429f2014-12-13 00:08:19 +00001518 timed_out = set(timed_out)
Chaoren Line80372a2015-08-12 18:02:53 +00001519 num_test_files = len(passed) + len(failed)
1520 num_test_cases = pass_count + fail_count
Daniel Maleab42556f2013-04-19 18:32:53 +00001521
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001522 # move core files into session dir
1523 cores = find('core.*', test_subdir)
1524 for core in cores:
1525 dst = core.replace(test_directory, "")[1:]
1526 dst = dst.replace(os.path.sep, "-")
1527 os.rename(core, os.path.join(session_dir, dst))
1528
Vince Harron06381732015-05-12 23:10:36 +00001529 # remove expected timeouts from failures
Vince Harron06381732015-05-12 23:10:36 +00001530 for xtime in expected_timeout:
1531 if xtime in timed_out:
1532 timed_out.remove(xtime)
1533 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001534 result = "ExpectedTimeout"
1535 elif xtime in passed:
1536 result = "UnexpectedCompletion"
1537 else:
1538 result = None # failed
1539
1540 if result:
1541 test_name = os.path.splitext(xtime)[0]
1542 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +00001543
Todd Fiala46a4e342015-12-02 18:48:38 +00001544 # Only run the old summary logic if we don't have a results formatter
1545 # that already prints the summary.
1546 if results_formatter is None or not results_formatter.replaces_summary():
1547 print_legacy_summary = True
1548 else:
1549 print_legacy_summary = False
Zachary Turner4cceca72015-08-14 16:45:32 +00001550
Todd Fiala46a4e342015-12-02 18:48:38 +00001551 if not print_legacy_summary:
Todd Fiala46a4e342015-12-02 18:48:38 +00001552 # Figure out exit code by count of test result types.
1553 issue_count = (
1554 results_formatter.counts_by_test_result_status(
Todd Fiala51831472015-12-09 06:45:43 +00001555 EventBuilder.STATUS_ERROR) +
Todd Fiala46a4e342015-12-02 18:48:38 +00001556 results_formatter.counts_by_test_result_status(
Todd Fiala51831472015-12-09 06:45:43 +00001557 EventBuilder.STATUS_FAILURE) +
1558 results_formatter.counts_by_test_result_status(
Todd Fiala0a7c32b2015-12-09 19:05:44 +00001559 EventBuilder.STATUS_TIMEOUT) +
1560 results_formatter.counts_by_test_result_status(
1561 EventBuilder.STATUS_EXCEPTIONAL_EXIT)
Todd Fiala51831472015-12-09 06:45:43 +00001562 )
1563
Todd Fiala46a4e342015-12-02 18:48:38 +00001564 # Return with appropriate result code
1565 if issue_count > 0:
1566 sys.exit(1)
1567 else:
1568 sys.exit(0)
1569 else:
1570 # Print the legacy test results summary.
1571 print()
1572 sys.stdout.write("Ran %d test suites" % num_test_files)
1573 if num_test_files > 0:
1574 sys.stdout.write(" (%d failed) (%f%%)" % (
1575 len(failed), 100.0 * len(failed) / num_test_files))
1576 print()
1577 sys.stdout.write("Ran %d test cases" % num_test_cases)
1578 if num_test_cases > 0:
1579 sys.stdout.write(" (%d failed) (%f%%)" % (
1580 fail_count, 100.0 * fail_count / num_test_cases))
1581 print()
1582 exit_code = 0
1583
1584 if len(failed) > 0:
1585 failed.sort()
1586 print("Failing Tests (%d)" % len(failed))
1587 for f in failed:
1588 print("%s: LLDB (suite) :: %s (%s)" % (
1589 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
1590 ))
1591 exit_code = 1
1592
1593 if len(unexpected_successes) > 0:
1594 unexpected_successes.sort()
1595 print("\nUnexpected Successes (%d)" % len(unexpected_successes))
1596 for u in unexpected_successes:
1597 print("UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info))
Zachary Turner4cceca72015-08-14 16:45:32 +00001598
1599 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001600
1601if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +00001602 sys.stderr.write(
1603 "error: dosep.py no longer supports being called directly. "
1604 "Please call dotest.py directly. The dosep.py-specific arguments "
1605 "have been added under the Parallel processing arguments.\n")
1606 sys.exit(128)