blob: 0861b709732646cf636c914f0340db8a25f88a54 [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
Zachary Turner38e64172015-08-10 17:46:11 +000075output_on_success = False
Todd Fiala68615ce2015-09-15 21:38:04 +000076RESULTS_FORMATTER = None
77RUNNER_PROCESS_ASYNC_MAP = None
78RESULTS_LISTENER_CHANNEL = None
Chaoren Linb6325d02015-08-12 18:02:54 +000079
Todd Fiala33896a92015-09-18 21:01:13 +000080"""Contains an optional function pointer that can return the worker index
81 for the given thread/process calling it. Returns a 0-based index."""
82GET_WORKER_INDEX = None
83
Todd Fiala1cc97b42015-09-21 05:42:26 +000084
Todd Fiala33896a92015-09-18 21:01:13 +000085def setup_global_variables(
86 lock, counter, total, name_len, options, worker_index_map):
Chaoren Linffc63b02015-08-12 18:02:49 +000087 global output_lock, test_counter, total_tests, test_name_len
88 global dotest_options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000089 output_lock = lock
90 test_counter = counter
91 total_tests = total
Chaoren Linffc63b02015-08-12 18:02:49 +000092 test_name_len = name_len
Pavel Labath05ab2372015-07-06 15:57:52 +000093 dotest_options = options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000094
Todd Fiala33896a92015-09-18 21:01:13 +000095 if worker_index_map is not None:
96 # We'll use the output lock for this to avoid sharing another lock.
97 # This won't be used much.
98 index_lock = lock
99
100 def get_worker_index_use_pid():
101 """Returns a 0-based, process-unique index for the worker."""
102 pid = os.getpid()
103 with index_lock:
104 if pid not in worker_index_map:
105 worker_index_map[pid] = len(worker_index_map)
106 return worker_index_map[pid]
107
108 global GET_WORKER_INDEX
109 GET_WORKER_INDEX = get_worker_index_use_pid
110
Zachary Turner38e64172015-08-10 17:46:11 +0000111def report_test_failure(name, command, output):
112 global output_lock
113 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000114 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
Zachary Turnerff890da2015-10-19 23:45:41 +0000115 print(file=sys.stderr)
116 print(output, file=sys.stderr)
117 print("[%s FAILED]" % name, file=sys.stderr)
118 print("Command invoked: %s" % ' '.join(command), file=sys.stderr)
Chaoren Linffc63b02015-08-12 18:02:49 +0000119 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000120
Chaoren Linb6325d02015-08-12 18:02:54 +0000121
Zachary Turner38e64172015-08-10 17:46:11 +0000122def report_test_pass(name, output):
123 global output_lock, output_on_success
124 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000125 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
126 if output_on_success:
Zachary Turnerff890da2015-10-19 23:45:41 +0000127 print(file=sys.stderr)
128 print(output, file=sys.stderr)
129 print("[%s PASSED]" % name, file=sys.stderr)
Chaoren Linffc63b02015-08-12 18:02:49 +0000130 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000131
Chaoren Linb6325d02015-08-12 18:02:54 +0000132
Chaoren Linffc63b02015-08-12 18:02:49 +0000133def update_progress(test_name=""):
134 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000135 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000136 counter_len = len(str(total_tests))
Greg Clayton1827fc22015-09-19 00:39:09 +0000137 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
138 sys.stderr.write(
139 "\r%*d out of %d test suites processed - %-*s" %
140 (counter_len, test_counter.value, total_tests,
141 test_name_len.value, test_name))
Chaoren Linffc63b02015-08-12 18:02:49 +0000142 if len(test_name) > test_name_len.value:
143 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000144 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000145 sys.stdout.flush()
146 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000147
Chaoren Linb6325d02015-08-12 18:02:54 +0000148
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000149def parse_test_results(output):
150 passes = 0
151 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000152 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000153 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000154 pass_count = re.search("^RESULT:.*([0-9]+) passes",
155 result, re.MULTILINE)
156 fail_count = re.search("^RESULT:.*([0-9]+) failures",
157 result, re.MULTILINE)
158 error_count = re.search("^RESULT:.*([0-9]+) errors",
159 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000160 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
161 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000162 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000163 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000164 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000165 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000166 if unexpected_success_count is not None:
167 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000168 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000169 failures = failures + int(error_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000170 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000171
Chaoren Linb6325d02015-08-12 18:02:54 +0000172
Todd Fiala2d3754d2015-09-29 22:19:06 +0000173class DoTestProcessDriver(process_control.ProcessDriver):
174 """Drives the dotest.py inferior process and handles bookkeeping."""
175 def __init__(self, output_file, output_file_lock, pid_events, file_name,
176 soft_terminate_timeout):
177 super(DoTestProcessDriver, self).__init__(
178 soft_terminate_timeout=soft_terminate_timeout)
179 self.output_file = output_file
180 self.output_lock = lldb_utils.OptionalWith(output_file_lock)
181 self.pid_events = pid_events
182 self.results = None
183 self.file_name = file_name
184
185 def write(self, content):
186 with self.output_lock:
187 self.output_file.write(content)
188
189 def on_process_started(self):
190 if self.pid_events:
191 self.pid_events.put_nowait(('created', self.process.pid))
192
193 def on_process_exited(self, command, output, was_timeout, exit_status):
194 if self.pid_events:
195 # No point in culling out those with no exit_status (i.e.
196 # those we failed to kill). That would just cause
197 # downstream code to try to kill it later on a Ctrl-C. At
198 # this point, a best-effort-to-kill already took place. So
199 # call it destroyed here.
200 self.pid_events.put_nowait(('destroyed', self.process.pid))
201
202 # Override the exit status if it was a timeout.
203 if was_timeout:
204 exit_status = eTimedOut
205
206 # If we didn't end up with any output, call it empty for
207 # stdout/stderr.
208 if output is None:
209 output = ('', '')
210
211 # Now parse the output.
212 passes, failures, unexpected_successes = parse_test_results(output)
213 if exit_status == 0:
214 # stdout does not have any useful information from 'dotest.py',
215 # only stderr does.
216 report_test_pass(self.file_name, output[1])
217 else:
218 report_test_failure(self.file_name, command, output[1])
219
220 # Save off the results for the caller.
221 self.results = (
222 self.file_name,
223 exit_status,
224 passes,
225 failures,
226 unexpected_successes)
227
Todd Fiala51831472015-12-09 06:45:43 +0000228 def is_exceptional_exit(self):
229 """Returns whether the process returned a timeout.
230
231 Not valid to call until after on_process_exited() completes.
232
233 @return True if the exit is an exceptional exit (e.g. signal on
234 POSIX); False otherwise.
235 """
236 if self.results is None:
237 raise Exception(
238 "exit status checked before results are available")
239 return self.process_helper.is_exceptional_exit(
240 self.results[1])
241
242 def exceptional_exit_details(self):
243 if self.results is None:
244 raise Exception(
245 "exit status checked before results are available")
246 return self.process_helper.exceptional_exit_details(self.results[1])
247
248 def is_timeout(self):
249 if self.results is None:
250 raise Exception(
251 "exit status checked before results are available")
252 return self.results[1] == eTimedOut
253
Todd Fiala2d3754d2015-09-29 22:19:06 +0000254
255def get_soft_terminate_timeout():
256 # Defaults to 10 seconds, but can set
257 # LLDB_TEST_SOFT_TERMINATE_TIMEOUT to a floating point
258 # number in seconds. This value indicates how long
259 # the test runner will wait for the dotest inferior to
260 # handle a timeout via a soft terminate before it will
261 # assume that failed and do a hard terminate.
262
263 # TODO plumb through command-line option
264 return float(os.environ.get('LLDB_TEST_SOFT_TERMINATE_TIMEOUT', 10.0))
265
266
267def want_core_on_soft_terminate():
268 # TODO plumb through command-line option
269 if platform.system() == 'Linux':
270 return True
271 else:
272 return False
Todd Fialada817b62015-09-22 18:05:11 +0000273
274
Todd Fiala51831472015-12-09 06:45:43 +0000275def send_events_to_collector(events, command):
276 """Sends the given events to the collector described in the command line.
277
278 @param events the list of events to send to the test event collector.
279 @param command the inferior command line which contains the details on
280 how to connect to the test event collector.
281 """
282 if events is None or len(events) == 0:
283 # Nothing to do.
284 return
285
286 # Find the port we need to connect to from the --results-port option.
287 try:
288 arg_index = command.index("--results-port") + 1
289 except ValueError:
290 # There is no results port, so no way to communicate back to
291 # the event collector. This is not a problem if we're not
292 # using event aggregation.
293 # TODO flag as error once we always use the event system
294 print(
295 "INFO: no event collector, skipping post-inferior test "
296 "event reporting")
297 return
298
299 if arg_index >= len(command):
300 raise Exception(
301 "expected collector port at index {} in {}".format(
302 arg_index, command))
303 event_port = int(command[arg_index])
304
305 # Create results formatter connected back to collector via socket.
306 config = result_formatter.FormatterConfig()
307 config.port = event_port
308 formatter_spec = result_formatter.create_results_formatter(config)
309 if formatter_spec is None or formatter_spec.formatter is None:
310 raise Exception(
311 "Failed to create socket-based ResultsFormatter "
312 "back to test event collector")
313
314 # Send the events: the port-based event just pickles the content
315 # and sends over to the server side of the socket.
316 for event in events:
317 formatter_spec.formatter.handle_event(event)
318
319 # Cleanup
320 if formatter_spec.cleanup_func is not None:
321 formatter_spec.cleanup_func()
322
323
324def send_inferior_post_run_events(command, worker_index, process_driver):
325 """Sends any test events that should be generated after the inferior runs.
326
327 These events would include timeouts and exceptional (i.e. signal-returning)
328 process completion results.
329
330 @param command the list of command parameters passed to subprocess.Popen().
331 @param worker_index the worker index (possibly None) used to run
332 this process
333 @param process_driver the ProcessDriver-derived instance that was used
334 to run the inferior process.
335 """
336 if process_driver is None:
337 raise Exception("process_driver must not be None")
338 if process_driver.results is None:
339 # Invalid condition - the results should have been set one way or
340 # another, even in a timeout.
341 raise Exception("process_driver.results were not set")
342
343 # The code below fills in the post events struct. If there are any post
344 # events to fire up, we'll try to make a connection to the socket and
345 # provide the results.
346 post_events = []
347
348 # Handle signal/exceptional exits.
349 if process_driver.is_exceptional_exit():
350 (code, desc) = process_driver.exceptional_exit_details()
351 test_filename = process_driver.results[0]
352 post_events.append(
353 EventBuilder.event_for_job_exceptional_exit(
354 process_driver.pid,
355 worker_index,
356 code,
357 desc,
358 test_filename,
359 command))
360
361 # Handle timeouts.
362 if process_driver.is_timeout():
363 test_filename = process_driver.results[0]
364 post_events.append(EventBuilder.event_for_job_timeout(
365 process_driver.pid,
366 worker_index,
367 test_filename,
368 command))
369
370 if len(post_events) > 0:
371 send_events_to_collector(post_events, command)
372
373
Todd Fiala8cbeed32015-09-08 22:22:33 +0000374def call_with_timeout(command, timeout, name, inferior_pid_events):
Todd Fiala2d3754d2015-09-29 22:19:06 +0000375 # Add our worker index (if we have one) to all test events
376 # from this inferior.
Todd Fiala51831472015-12-09 06:45:43 +0000377 worker_index = None
Todd Fiala33896a92015-09-18 21:01:13 +0000378 if GET_WORKER_INDEX is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000379 try:
380 worker_index = GET_WORKER_INDEX()
381 command.extend([
Todd Fiala2d3754d2015-09-29 22:19:06 +0000382 "--event-add-entries",
383 "worker_index={}:int".format(worker_index)])
384 except: # pylint: disable=bare-except
385 # Ctrl-C does bad things to multiprocessing.Manager.dict()
386 # lookup. Just swallow it.
Todd Fialae83f1402015-09-18 22:45:31 +0000387 pass
388
Todd Fiala2d3754d2015-09-29 22:19:06 +0000389 # Create the inferior dotest.py ProcessDriver.
390 soft_terminate_timeout = get_soft_terminate_timeout()
391 want_core = want_core_on_soft_terminate()
Todd Fiala68615ce2015-09-15 21:38:04 +0000392
Todd Fiala2d3754d2015-09-29 22:19:06 +0000393 process_driver = DoTestProcessDriver(
394 sys.stdout,
395 output_lock,
396 inferior_pid_events,
397 name,
398 soft_terminate_timeout)
Todd Fiala68615ce2015-09-15 21:38:04 +0000399
Todd Fiala2d3754d2015-09-29 22:19:06 +0000400 # Run it with a timeout.
401 process_driver.run_command_with_timeout(command, timeout, want_core)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000402
Todd Fiala2d3754d2015-09-29 22:19:06 +0000403 # Return the results.
404 if not process_driver.results:
405 # This is truly exceptional. Even a failing or timed out
406 # binary should have called the results-generation code.
407 raise Exception("no test results were generated whatsoever")
Todd Fiala51831472015-12-09 06:45:43 +0000408
409 # Handle cases where the test inferior cannot adequately provide
410 # meaningful results to the test event system.
411 send_inferior_post_run_events(
412 command,
413 worker_index,
414 process_driver)
415
416
Todd Fiala2d3754d2015-09-29 22:19:06 +0000417 return process_driver.results
Johnny Chene8d9dc62011-10-31 19:04:07 +0000418
Chaoren Linb6325d02015-08-12 18:02:54 +0000419
Zachary Turner7d564542015-11-02 19:19:49 +0000420def process_dir(root, files, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000421 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000422 results = []
Steve Puccibefe2b12014-03-07 00:01:11 +0000423 for name in files:
Zachary Turner7d564542015-11-02 19:19:49 +0000424 import __main__ as main
425 script_file = main.__file__
Zachary Turnerf6896b02015-01-05 19:37:03 +0000426 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000427 dotest_argv +
Todd Fialafed95662015-09-03 18:58:44 +0000428 ["--inferior", "-p", name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000429
430 timeout_name = os.path.basename(os.path.splitext(name)[0]).upper()
431
Chaoren Linb6325d02015-08-12 18:02:54 +0000432 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
433 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000434
Todd Fiala8cbeed32015-09-08 22:22:33 +0000435 results.append(call_with_timeout(
436 command, timeout, name, inferior_pid_events))
Vince Harron17f429f2014-12-13 00:08:19 +0000437
Zachary Turner4cceca72015-08-14 16:45:32 +0000438 # result = (name, status, passes, failures, unexpected_successes)
439 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000440 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000441 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000442 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000443 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000444 if status != ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000445 unexpected_passes = [name for name, _, _, _, unexpected_successes in results
446 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000447
Chaoren Line80372a2015-08-12 18:02:53 +0000448 pass_count = sum([result[2] for result in results])
449 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000450
Zachary Turner4cceca72015-08-14 16:45:32 +0000451 return (timed_out, passed, failed, unexpected_passes, pass_count, fail_count)
Steve Puccibefe2b12014-03-07 00:01:11 +0000452
453in_q = None
454out_q = None
455
Chaoren Linb6325d02015-08-12 18:02:54 +0000456
Todd Fiala8cbeed32015-09-08 22:22:33 +0000457def process_dir_worker_multiprocessing(
458 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000459 a_dotest_options, job_queue, result_queue, inferior_pid_events,
460 worker_index_map):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000461 """Worker thread main loop when in multiprocessing mode.
Steve Puccibefe2b12014-03-07 00:01:11 +0000462 Takes one directory specification at a time and works on it."""
Todd Fiala8cbeed32015-09-08 22:22:33 +0000463
464 # Shut off interrupt handling in the child process.
465 signal.signal(signal.SIGINT, signal.SIG_IGN)
Ying Chend93aa102015-09-23 21:53:18 +0000466 if hasattr(signal, 'SIGHUP'):
467 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000468
469 # Setup the global state for the worker process.
470 setup_global_variables(
471 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000472 a_dotest_options, worker_index_map)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000473
474 # Keep grabbing entries from the queue until done.
475 while not job_queue.empty():
476 try:
477 job = job_queue.get(block=False)
Pavel Labath48c6b522015-11-02 20:54:25 +0000478 result = process_dir(job[0], job[1], job[2],
Todd Fiala8cbeed32015-09-08 22:22:33 +0000479 inferior_pid_events)
480 result_queue.put(result)
Zachary Turner814236d2015-10-21 17:48:52 +0000481 except queue.Empty:
Todd Fiala8cbeed32015-09-08 22:22:33 +0000482 # Fine, we're done.
483 pass
Steve Puccibefe2b12014-03-07 00:01:11 +0000484
Chaoren Linb6325d02015-08-12 18:02:54 +0000485
Todd Fiala8cbeed32015-09-08 22:22:33 +0000486def process_dir_worker_multiprocessing_pool(args):
487 return process_dir(*args)
488
489
Todd Fiala68615ce2015-09-15 21:38:04 +0000490def process_dir_worker_threading(job_queue, result_queue, inferior_pid_events):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000491 """Worker thread main loop when in threading mode.
492
493 This one supports the hand-rolled pooling support.
494
495 Takes one directory specification at a time and works on it."""
496
497 # Keep grabbing entries from the queue until done.
498 while not job_queue.empty():
499 try:
500 job = job_queue.get(block=False)
Pavel Labath48c6b522015-11-02 20:54:25 +0000501 result = process_dir(job[0], job[1], job[2],
Todd Fiala8cbeed32015-09-08 22:22:33 +0000502 inferior_pid_events)
503 result_queue.put(result)
Zachary Turner814236d2015-10-21 17:48:52 +0000504 except queue.Empty:
Todd Fiala8cbeed32015-09-08 22:22:33 +0000505 # Fine, we're done.
506 pass
507
508
509def process_dir_worker_threading_pool(args):
510 return process_dir(*args)
511
512
513def process_dir_mapper_inprocess(args):
514 """Map adapter for running the subprocess-based, non-threaded test runner.
515
516 @param args the process work item tuple
517 @return the test result tuple
518 """
519 return process_dir(*args)
520
521
522def collect_active_pids_from_pid_events(event_queue):
523 """
524 Returns the set of what should be active inferior pids based on
525 the event stream.
526
527 @param event_queue a multiprocessing.Queue containing events of the
528 form:
529 ('created', pid)
530 ('destroyed', pid)
531
532 @return set of inferior dotest.py pids activated but never completed.
533 """
534 active_pid_set = set()
535 while not event_queue.empty():
536 pid_event = event_queue.get_nowait()
537 if pid_event[0] == 'created':
538 active_pid_set.add(pid_event[1])
539 elif pid_event[0] == 'destroyed':
540 active_pid_set.remove(pid_event[1])
541 return active_pid_set
542
543
544def kill_all_worker_processes(workers, inferior_pid_events):
545 """
546 Kills all specified worker processes and their process tree.
547
548 @param workers a list of multiprocess.Process worker objects.
549 @param inferior_pid_events a multiprocess.Queue that contains
550 all inferior create and destroy events. Used to construct
551 the list of child pids still outstanding that need to be killed.
552 """
553 for worker in workers:
554 worker.terminate()
555 worker.join()
556
557 # Add all the child test pids created.
558 active_pid_set = collect_active_pids_from_pid_events(
559 inferior_pid_events)
560 for inferior_pid in active_pid_set:
Zachary Turnerff890da2015-10-19 23:45:41 +0000561 print("killing inferior pid {}".format(inferior_pid))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000562 os.kill(inferior_pid, signal.SIGKILL)
563
564
565def kill_all_worker_threads(workers, inferior_pid_events):
566 """
567 Kills all specified worker threads and their process tree.
568
569 @param workers a list of multiprocess.Process worker objects.
570 @param inferior_pid_events a multiprocess.Queue that contains
571 all inferior create and destroy events. Used to construct
572 the list of child pids still outstanding that need to be killed.
573 """
574
575 # Add all the child test pids created.
576 active_pid_set = collect_active_pids_from_pid_events(
577 inferior_pid_events)
578 for inferior_pid in active_pid_set:
Zachary Turnerff890da2015-10-19 23:45:41 +0000579 print("killing inferior pid {}".format(inferior_pid))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000580 os.kill(inferior_pid, signal.SIGKILL)
581
582 # We don't have a way to nuke the threads. However, since we killed
583 # all the inferiors, and we drained the job queue, this will be
584 # good enough. Wait cleanly for each worker thread to wrap up.
585 for worker in workers:
586 worker.join()
587
588
589def find_test_files_in_dir_tree(dir_root, found_func):
590 """Calls found_func for all the test files in the given dir hierarchy.
591
592 @param dir_root the path to the directory to start scanning
593 for test files. All files in this directory and all its children
594 directory trees will be searched.
595
596 @param found_func a callable object that will be passed
597 the parent directory (relative to dir_root) and the list of
598 test files from within that directory.
599 """
600 for root, _, files in os.walk(dir_root, topdown=False):
601 def is_test_filename(test_dir, base_filename):
602 """Returns True if the given filename matches the test name format.
603
604 @param test_dir the directory to check. Should be absolute or
605 relative to current working directory.
606
607 @param base_filename the base name of the filename to check for a
608 dherence to the python test case filename format.
609
610 @return True if name matches the python test case filename format.
611 """
612 # Not interested in symbolically linked files.
613 if os.path.islink(os.path.join(test_dir, base_filename)):
614 return False
615 # Only interested in test files with the "Test*.py" naming pattern.
616 return (base_filename.startswith("Test") and
617 base_filename.endswith(".py"))
618
619 tests = [filename for filename in files
620 if is_test_filename(root, filename)]
621 if tests:
622 found_func(root, tests)
623
624
625def initialize_global_vars_common(num_threads, test_work_items):
626 global total_tests, test_counter, test_name_len
Todd Fiala51831472015-12-09 06:45:43 +0000627
Todd Fiala8cbeed32015-09-08 22:22:33 +0000628 total_tests = sum([len(item[1]) for item in test_work_items])
629 test_counter = multiprocessing.Value('i', 0)
630 test_name_len = multiprocessing.Value('i', 0)
Greg Clayton1827fc22015-09-19 00:39:09 +0000631 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
Zachary Turnerff890da2015-10-19 23:45:41 +0000632 print("Testing: %d test suites, %d thread%s" % (
633 total_tests, num_threads, (num_threads > 1) * "s"), file=sys.stderr)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000634 update_progress()
635
636
637def initialize_global_vars_multiprocessing(num_threads, test_work_items):
638 # Initialize the global state we'll use to communicate with the
639 # rest of the flat module.
640 global output_lock
641 output_lock = multiprocessing.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000642
Todd Fiala8cbeed32015-09-08 22:22:33 +0000643 initialize_global_vars_common(num_threads, test_work_items)
644
645
646def initialize_global_vars_threading(num_threads, test_work_items):
Todd Fiala33896a92015-09-18 21:01:13 +0000647 """Initializes global variables used in threading mode.
648 @param num_threads specifies the number of workers used.
649 @param test_work_items specifies all the work items
650 that will be processed.
651 """
Todd Fiala8cbeed32015-09-08 22:22:33 +0000652 # Initialize the global state we'll use to communicate with the
653 # rest of the flat module.
654 global output_lock
655 output_lock = threading.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000656
657 index_lock = threading.RLock()
658 index_map = {}
659
660 def get_worker_index_threading():
661 """Returns a 0-based, thread-unique index for the worker thread."""
662 thread_id = threading.current_thread().ident
663 with index_lock:
664 if thread_id not in index_map:
665 index_map[thread_id] = len(index_map)
666 return index_map[thread_id]
667
668
669 global GET_WORKER_INDEX
670 GET_WORKER_INDEX = get_worker_index_threading
671
Todd Fiala8cbeed32015-09-08 22:22:33 +0000672 initialize_global_vars_common(num_threads, test_work_items)
673
674
Todd Fiala68615ce2015-09-15 21:38:04 +0000675def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler):
676 """Provides a main loop that is Ctrl-C protected.
677
678 The main loop calls the main_op_func() repeatedly until done_func()
679 returns true. The ctrl_c_handler() method is called with a single
680 int parameter that contains the number of times the ctrl_c has been
681 hit (starting with 1). The ctrl_c_handler() should mutate whatever
682 it needs to have the done_func() return True as soon as it is desired
683 to exit the loop.
684 """
685 done = False
686 ctrl_c_count = 0
687
688 while not done:
689 try:
690 # See if we're done. Start with done check since it is
691 # the first thing executed after a Ctrl-C handler in the
692 # following loop.
693 done = done_func()
694 if not done:
695 # Run the main op once.
696 main_op_func()
697
698 except KeyboardInterrupt:
699 ctrl_c_count += 1
700 ctrl_c_handler(ctrl_c_count)
701
702
703def pump_workers_and_asyncore_map(workers, asyncore_map):
704 """Prunes out completed workers and maintains the asyncore loop.
705
706 The asyncore loop contains the optional socket listener
707 and handlers. When all workers are complete, this method
708 takes care of stopping the listener. It also runs the
709 asyncore loop for the given async map for 10 iterations.
710
711 @param workers the list of worker Thread/Process instances.
712
713 @param asyncore_map the asyncore threading-aware map that
714 indicates which channels are in use and still alive.
715 """
716
717 # Check on all the workers, removing them from the workers
718 # list as they complete.
719 dead_workers = []
720 for worker in workers:
721 # This non-blocking join call is what allows us
722 # to still receive keyboard interrupts.
723 worker.join(0.01)
724 if not worker.is_alive():
725 dead_workers.append(worker)
726 # Clear out the completed workers
727 for dead_worker in dead_workers:
728 workers.remove(dead_worker)
729
730 # If there are no more workers and there is a listener,
731 # close the listener.
732 global RESULTS_LISTENER_CHANNEL
733 if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None:
734 RESULTS_LISTENER_CHANNEL.close()
735 RESULTS_LISTENER_CHANNEL = None
736
737 # Pump the asyncore map if it isn't empty.
738 if len(asyncore_map) > 0:
739 asyncore.loop(0.1, False, asyncore_map, 10)
740
741
742def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events,
743 stop_all_inferiors_func):
744 """Performs the appropriate ctrl-c action for non-pool parallel test runners
745
746 @param ctrl_c_count starting with 1, indicates the number of times ctrl-c
747 has been intercepted. The value is 1 on the first intercept, 2 on the
748 second, etc.
749
750 @param job_queue a Queue object that contains the work still outstanding
751 (i.e. hasn't been assigned to a worker yet).
752
753 @param workers list of Thread or Process workers.
754
755 @param inferior_pid_events specifies a Queue of inferior process
756 construction and destruction events. Used to build the list of inferior
757 processes that should be killed if we get that far.
758
759 @param stop_all_inferiors_func a callable object that takes the
760 workers and inferior_pid_events parameters (in that order) if a hard
761 stop is to be used on the workers.
762 """
763
764 # Print out which Ctrl-C we're handling.
765 key_name = [
766 "first",
767 "second",
768 "third",
769 "many"]
770
771 if ctrl_c_count < len(key_name):
772 name_index = ctrl_c_count - 1
773 else:
774 name_index = len(key_name) - 1
775 message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index])
776 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000777 print(message)
Todd Fiala68615ce2015-09-15 21:38:04 +0000778
779 if ctrl_c_count == 1:
780 # Remove all outstanding items from the work queue so we stop
781 # doing any more new work.
782 while not job_queue.empty():
783 try:
784 # Just drain it to stop more work from being started.
785 job_queue.get_nowait()
Zachary Turner814236d2015-10-21 17:48:52 +0000786 except queue.Empty:
Todd Fiala68615ce2015-09-15 21:38:04 +0000787 pass
788 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000789 print("Stopped more work from being started.")
Todd Fiala68615ce2015-09-15 21:38:04 +0000790 elif ctrl_c_count == 2:
791 # Try to stop all inferiors, even the ones currently doing work.
792 stop_all_inferiors_func(workers, inferior_pid_events)
793 else:
794 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000795 print("All teardown activities kicked off, should finish soon.")
Todd Fiala68615ce2015-09-15 21:38:04 +0000796
797
798def workers_and_async_done(workers, async_map):
799 """Returns True if the workers list and asyncore channels are all done.
800
801 @param workers list of workers (threads/processes). These must adhere
802 to the threading Thread or multiprocessing.Process interface.
803
804 @param async_map the threading-aware asyncore channel map to check
805 for live channels.
806
807 @return False if the workers list exists and has any entries in it, or
808 if the async_map exists and has any entries left in it; otherwise, True.
809 """
810 if workers is not None and len(workers) > 0:
811 # We're not done if we still have workers left.
812 return False
813 if async_map is not None and len(async_map) > 0:
814 return False
815 # We're done.
816 return True
817
818
Todd Fiala8cbeed32015-09-08 22:22:33 +0000819def multiprocessing_test_runner(num_threads, test_work_items):
820 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
821
822 This concurrent test runner is based on the multiprocessing
823 library, and rolls its own worker pooling strategy so it
824 can handle Ctrl-C properly.
825
826 This test runner is known to have an issue running on
827 Windows platforms.
828
829 @param num_threads the number of worker processes to use.
830
831 @param test_work_items the iterable of test work item tuples
832 to run.
833 """
834
835 # Initialize our global state.
836 initialize_global_vars_multiprocessing(num_threads, test_work_items)
837
838 # Create jobs.
839 job_queue = multiprocessing.Queue(len(test_work_items))
840 for test_work_item in test_work_items:
841 job_queue.put(test_work_item)
842
843 result_queue = multiprocessing.Queue(len(test_work_items))
844
845 # Create queues for started child pids. Terminating
846 # the multiprocess processes does not terminate the
847 # child processes they spawn. We can remove this tracking
848 # if/when we move to having the multiprocess process directly
849 # perform the test logic. The Queue size needs to be able to
850 # hold 2 * (num inferior dotest.py processes started) entries.
851 inferior_pid_events = multiprocessing.Queue(4096)
852
Todd Fiala33896a92015-09-18 21:01:13 +0000853 # Worker dictionary allows each worker to figure out its worker index.
854 manager = multiprocessing.Manager()
855 worker_index_map = manager.dict()
856
Todd Fiala8cbeed32015-09-08 22:22:33 +0000857 # Create workers. We don't use multiprocessing.Pool due to
858 # challenges with handling ^C keyboard interrupts.
859 workers = []
860 for _ in range(num_threads):
861 worker = multiprocessing.Process(
862 target=process_dir_worker_multiprocessing,
863 args=(output_lock,
864 test_counter,
865 total_tests,
866 test_name_len,
867 dotest_options,
868 job_queue,
869 result_queue,
Todd Fiala33896a92015-09-18 21:01:13 +0000870 inferior_pid_events,
871 worker_index_map))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000872 worker.start()
873 workers.append(worker)
874
Todd Fiala68615ce2015-09-15 21:38:04 +0000875 # Main loop: wait for all workers to finish and wait for
876 # the socket handlers to wrap up.
877 ctrl_c_loop(
878 # Main operation of loop
879 lambda: pump_workers_and_asyncore_map(
880 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000881
Todd Fiala68615ce2015-09-15 21:38:04 +0000882 # Return True when we're done with the main loop.
883 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000884
Todd Fiala68615ce2015-09-15 21:38:04 +0000885 # Indicate what we do when we receive one or more Ctrl-Cs.
886 lambda ctrl_c_count: handle_ctrl_c(
887 ctrl_c_count, job_queue, workers, inferior_pid_events,
888 kill_all_worker_processes))
889
890 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000891 test_results = []
892 while not result_queue.empty():
893 test_results.append(result_queue.get(block=False))
894 return test_results
895
896
Todd Fiala68615ce2015-09-15 21:38:04 +0000897def map_async_run_loop(future, channel_map, listener_channel):
898 """Blocks until the Pool.map_async completes and the channel completes.
899
900 @param future an AsyncResult instance from a Pool.map_async() call.
901
902 @param channel_map the asyncore dispatch channel map that should be pumped.
903 Optional: may be None.
904
905 @param listener_channel the channel representing a listener that should be
906 closed once the map_async results are available.
907
908 @return the results from the async_result instance.
909 """
910 map_results = None
911
912 done = False
913 while not done:
914 # Check if we need to reap the map results.
915 if map_results is None:
916 if future.ready():
917 # Get the results.
918 map_results = future.get()
919
920 # Close the runner process listener channel if we have
921 # one: no more connections will be incoming.
922 if listener_channel is not None:
923 listener_channel.close()
924
925 # Pump the asyncore loop if we have a listener socket.
926 if channel_map is not None:
927 asyncore.loop(0.01, False, channel_map, 10)
928
929 # Figure out if we're done running.
930 done = map_results is not None
931 if channel_map is not None:
932 # We have a runner process async map. Check if it
933 # is complete.
934 if len(channel_map) > 0:
935 # We still have an asyncore channel running. Not done yet.
936 done = False
937
938 return map_results
939
940
Todd Fiala8cbeed32015-09-08 22:22:33 +0000941def multiprocessing_test_runner_pool(num_threads, test_work_items):
942 # Initialize our global state.
943 initialize_global_vars_multiprocessing(num_threads, test_work_items)
944
Todd Fiala33896a92015-09-18 21:01:13 +0000945 manager = multiprocessing.Manager()
946 worker_index_map = manager.dict()
947
Todd Fiala8cbeed32015-09-08 22:22:33 +0000948 pool = multiprocessing.Pool(
949 num_threads,
950 initializer=setup_global_variables,
951 initargs=(output_lock, test_counter, total_tests, test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000952 dotest_options, worker_index_map))
Todd Fiala68615ce2015-09-15 21:38:04 +0000953
954 # Start the map operation (async mode).
955 map_future = pool.map_async(
956 process_dir_worker_multiprocessing_pool, test_work_items)
957 return map_async_run_loop(
958 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000959
960
961def threading_test_runner(num_threads, test_work_items):
962 """Provides hand-wrapped pooling threading-based test runner adapter
963 with Ctrl-C support.
964
965 This concurrent test runner is based on the threading
966 library, and rolls its own worker pooling strategy so it
967 can handle Ctrl-C properly.
968
969 @param num_threads the number of worker processes to use.
970
971 @param test_work_items the iterable of test work item tuples
972 to run.
973 """
974
975 # Initialize our global state.
976 initialize_global_vars_threading(num_threads, test_work_items)
977
978 # Create jobs.
Zachary Turner814236d2015-10-21 17:48:52 +0000979 job_queue = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000980 for test_work_item in test_work_items:
981 job_queue.put(test_work_item)
982
Zachary Turner814236d2015-10-21 17:48:52 +0000983 result_queue = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000984
985 # Create queues for started child pids. Terminating
986 # the threading threads does not terminate the
987 # child processes they spawn.
Zachary Turner814236d2015-10-21 17:48:52 +0000988 inferior_pid_events = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000989
990 # Create workers. We don't use multiprocessing.pool.ThreadedPool
991 # due to challenges with handling ^C keyboard interrupts.
992 workers = []
993 for _ in range(num_threads):
994 worker = threading.Thread(
995 target=process_dir_worker_threading,
Todd Fiala68615ce2015-09-15 21:38:04 +0000996 args=(job_queue,
Todd Fiala8cbeed32015-09-08 22:22:33 +0000997 result_queue,
998 inferior_pid_events))
999 worker.start()
1000 workers.append(worker)
1001
Todd Fiala68615ce2015-09-15 21:38:04 +00001002 # Main loop: wait for all workers to finish and wait for
1003 # the socket handlers to wrap up.
1004 ctrl_c_loop(
1005 # Main operation of loop
1006 lambda: pump_workers_and_asyncore_map(
1007 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +00001008
Todd Fiala68615ce2015-09-15 21:38:04 +00001009 # Return True when we're done with the main loop.
1010 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +00001011
Todd Fiala68615ce2015-09-15 21:38:04 +00001012 # Indicate what we do when we receive one or more Ctrl-Cs.
1013 lambda ctrl_c_count: handle_ctrl_c(
1014 ctrl_c_count, job_queue, workers, inferior_pid_events,
1015 kill_all_worker_threads))
Todd Fiala8cbeed32015-09-08 22:22:33 +00001016
Todd Fiala68615ce2015-09-15 21:38:04 +00001017 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001018 test_results = []
1019 while not result_queue.empty():
1020 test_results.append(result_queue.get(block=False))
1021 return test_results
1022
1023
1024def threading_test_runner_pool(num_threads, test_work_items):
1025 # Initialize our global state.
1026 initialize_global_vars_threading(num_threads, test_work_items)
1027
Todd Fiala68615ce2015-09-15 21:38:04 +00001028 pool = multiprocessing.pool.ThreadPool(num_threads)
1029 map_future = pool.map_async(
1030 process_dir_worker_threading_pool, test_work_items)
1031
1032 return map_async_run_loop(
1033 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
1034
1035
1036def asyncore_run_loop(channel_map):
1037 try:
1038 asyncore.loop(None, False, channel_map)
1039 except:
1040 # Swallow it, we're seeing:
1041 # error: (9, 'Bad file descriptor')
1042 # when the listener channel is closed. Shouldn't be the case.
1043 pass
Todd Fiala8cbeed32015-09-08 22:22:33 +00001044
1045
1046def inprocess_exec_test_runner(test_work_items):
1047 # Initialize our global state.
1048 initialize_global_vars_multiprocessing(1, test_work_items)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001049
Todd Fiala33896a92015-09-18 21:01:13 +00001050 # We're always worker index 0
1051 global GET_WORKER_INDEX
1052 GET_WORKER_INDEX = lambda: 0
1053
Todd Fiala68615ce2015-09-15 21:38:04 +00001054 # Run the listener and related channel maps in a separate thread.
1055 # global RUNNER_PROCESS_ASYNC_MAP
1056 global RESULTS_LISTENER_CHANNEL
1057 if RESULTS_LISTENER_CHANNEL is not None:
1058 socket_thread = threading.Thread(
1059 target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP))
1060 socket_thread.start()
1061
1062 # Do the work.
Zachary Turner1c4059a2015-10-22 20:39:59 +00001063 test_results = list(map(process_dir_mapper_inprocess, test_work_items))
Todd Fiala68615ce2015-09-15 21:38:04 +00001064
1065 # If we have a listener channel, shut it down here.
1066 if RESULTS_LISTENER_CHANNEL is not None:
1067 # Close down the channel.
1068 RESULTS_LISTENER_CHANNEL.close()
1069 RESULTS_LISTENER_CHANNEL = None
1070
1071 # Wait for the listener and handlers to complete.
1072 socket_thread.join()
1073
1074 return test_results
Todd Fiala8cbeed32015-09-08 22:22:33 +00001075
1076def walk_and_invoke(test_directory, test_subdir, dotest_argv,
Todd Fiala871b2e52015-09-22 22:47:34 +00001077 num_workers, test_runner_func):
Steve Puccibefe2b12014-03-07 00:01:11 +00001078 """Look for matched files and invoke test driver on each one.
1079 In single-threaded mode, each test driver is invoked directly.
1080 In multi-threaded mode, submit each test driver to a worker
Vince Harrone06a7a82015-05-12 23:12:19 +00001081 queue, and then wait for all to complete.
1082
1083 test_directory - lldb/test/ directory
Chaoren Linb6325d02015-08-12 18:02:54 +00001084 test_subdir - lldb/test/ or a subfolder with the tests we're interested in
1085 running
Vince Harrone06a7a82015-05-12 23:12:19 +00001086 """
Todd Fiala68615ce2015-09-15 21:38:04 +00001087 # The async_map is important to keep all thread-related asyncore
1088 # channels distinct when we call asyncore.loop() later on.
1089 global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP
1090 RUNNER_PROCESS_ASYNC_MAP = {}
1091
1092 # If we're outputting side-channel test results, create the socket
1093 # listener channel and tell the inferior to send results to the
1094 # port on which we'll be listening.
1095 if RESULTS_FORMATTER is not None:
Todd Fialae83f1402015-09-18 22:45:31 +00001096 forwarding_func = RESULTS_FORMATTER.handle_event
Todd Fiala68615ce2015-09-15 21:38:04 +00001097 RESULTS_LISTENER_CHANNEL = (
1098 dotest_channels.UnpicklingForwardingListenerChannel(
Todd Fiala871b2e52015-09-22 22:47:34 +00001099 RUNNER_PROCESS_ASYNC_MAP, "localhost", 0,
1100 2 * num_workers, forwarding_func))
Todd Fiala68615ce2015-09-15 21:38:04 +00001101 dotest_argv.append("--results-port")
1102 dotest_argv.append(str(RESULTS_LISTENER_CHANNEL.address[1]))
Todd Fiala3f0a3602014-07-08 06:42:37 +00001103
1104 # Collect the test files that we'll run.
1105 test_work_items = []
Todd Fiala8cbeed32015-09-08 22:22:33 +00001106 find_test_files_in_dir_tree(
1107 test_subdir, lambda testdir, test_files: test_work_items.append([
Zachary Turner7d564542015-11-02 19:19:49 +00001108 test_subdir, test_files, dotest_argv, None]))
Todd Fiala3f0a3602014-07-08 06:42:37 +00001109
Todd Fiala8cbeed32015-09-08 22:22:33 +00001110 # Convert test work items into test results using whatever
1111 # was provided as the test run function.
1112 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +00001113
Todd Fiala8cbeed32015-09-08 22:22:33 +00001114 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +00001115 timed_out = sum([result[0] for result in test_results], [])
1116 passed = sum([result[1] for result in test_results], [])
1117 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +00001118 unexpected_successes = sum([result[3] for result in test_results], [])
1119 pass_count = sum([result[4] for result in test_results])
1120 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +00001121
Todd Fiala8cbeed32015-09-08 22:22:33 +00001122 return (timed_out, passed, failed, unexpected_successes, pass_count,
1123 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001124
Chaoren Linb6325d02015-08-12 18:02:54 +00001125
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001126def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +00001127 # returns a set of test filenames that might timeout
1128 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +00001129 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001130 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +00001131 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001132 else:
Todd Fiala68615ce2015-09-15 21:38:04 +00001133 m = re.search(r'remote-(\w+)', platform_name)
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001134 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +00001135
1136 expected_timeout = set()
1137
1138 if target.startswith("linux"):
1139 expected_timeout |= {
Vince Harron06381732015-05-12 23:10:36 +00001140 "TestConnectRemote.py",
1141 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +00001142 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +00001143 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001144
1145 # Times out in ~10% of the times on the build bot
1146 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +00001147 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001148 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +00001149 "TestThreadStepOut.py",
1150 }
1151 elif target.startswith("android"):
1152 expected_timeout |= {
1153 "TestExitDuringStep.py",
1154 "TestHelloWorld.py",
1155 }
Chaoren Linfebef1b2015-08-19 17:22:12 +00001156 if host.startswith("win32"):
1157 expected_timeout |= {
1158 "TestEvents.py",
1159 "TestThreadStates.py",
1160 }
Ed Maste4dd8fba2015-05-14 16:25:52 +00001161 elif target.startswith("freebsd"):
1162 expected_timeout |= {
1163 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +00001164 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +00001165 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +00001166 "TestWatchpointConditionAPI.py",
1167 }
Vince Harron0f173ac2015-05-18 19:36:33 +00001168 elif target.startswith("darwin"):
1169 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +00001170 # times out on MBP Retina, Mid 2012
1171 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +00001172 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +00001173 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +00001174 }
Vince Harron06381732015-05-12 23:10:36 +00001175 return expected_timeout
1176
Chaoren Linb6325d02015-08-12 18:02:54 +00001177
Pavel Labathfad30cf2015-06-29 14:16:51 +00001178def getDefaultTimeout(platform_name):
1179 if os.getenv("LLDB_TEST_TIMEOUT"):
1180 return os.getenv("LLDB_TEST_TIMEOUT")
1181
1182 if platform_name is None:
1183 platform_name = sys.platform
1184
1185 if platform_name.startswith("remote-"):
1186 return "10m"
Todd Fiala88722f72015-11-11 05:10:07 +00001187 elif platform_name == 'darwin':
1188 # We are consistently needing more time on a few tests.
1189 return "6m"
Pavel Labathfad30cf2015-06-29 14:16:51 +00001190 else:
1191 return "4m"
1192
Chaoren Linb6325d02015-08-12 18:02:54 +00001193
Vince Harron0b9dbb52015-05-21 18:18:52 +00001194def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +00001195 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +00001196 os.utime(fname, times)
1197
Chaoren Linb6325d02015-08-12 18:02:54 +00001198
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001199def find(pattern, path):
1200 result = []
1201 for root, dirs, files in os.walk(path):
1202 for name in files:
1203 if fnmatch.fnmatch(name, pattern):
1204 result.append(os.path.join(root, name))
1205 return result
1206
Chaoren Linb6325d02015-08-12 18:02:54 +00001207
Todd Fiala8cbeed32015-09-08 22:22:33 +00001208def get_test_runner_strategies(num_threads):
1209 """Returns the test runner strategies by name in a dictionary.
1210
1211 @param num_threads specifies the number of threads/processes
1212 that will be used for concurrent test runners.
1213
1214 @return dictionary with key as test runner strategy name and
1215 value set to a callable object that takes the test work item
1216 and returns a test result tuple.
1217 """
1218 return {
1219 # multiprocessing supports ctrl-c and does not use
1220 # multiprocessing.Pool.
1221 "multiprocessing":
1222 (lambda work_items: multiprocessing_test_runner(
1223 num_threads, work_items)),
1224
1225 # multiprocessing-pool uses multiprocessing.Pool but
1226 # does not support Ctrl-C.
1227 "multiprocessing-pool":
1228 (lambda work_items: multiprocessing_test_runner_pool(
1229 num_threads, work_items)),
1230
1231 # threading uses a hand-rolled worker pool much
1232 # like multiprocessing, but instead uses in-process
1233 # worker threads. This one supports Ctrl-C.
1234 "threading":
1235 (lambda work_items: threading_test_runner(num_threads, work_items)),
1236
1237 # threading-pool uses threading for the workers (in-process)
1238 # and uses the multiprocessing.pool thread-enabled pool.
Todd Fiala68615ce2015-09-15 21:38:04 +00001239 # This does not properly support Ctrl-C.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001240 "threading-pool":
1241 (lambda work_items: threading_test_runner_pool(
1242 num_threads, work_items)),
1243
1244 # serial uses the subprocess-based, single process
1245 # test runner. This provides process isolation but
Todd Fiala68615ce2015-09-15 21:38:04 +00001246 # no concurrent test execution.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001247 "serial":
1248 inprocess_exec_test_runner
1249 }
1250
1251
Todd Fialaea736242015-09-23 15:21:28 +00001252def _remove_option(
1253 args, long_option_name, short_option_name, takes_arg):
Todd Fiala68615ce2015-09-15 21:38:04 +00001254 """Removes option and related option arguments from args array.
Todd Fialaea736242015-09-23 15:21:28 +00001255
1256 This method removes all short/long options that match the given
1257 arguments.
1258
Todd Fiala68615ce2015-09-15 21:38:04 +00001259 @param args the array of command line arguments (in/out)
Todd Fialaea736242015-09-23 15:21:28 +00001260
1261 @param long_option_name the full command line representation of the
1262 long-form option that will be removed (including '--').
1263
1264 @param short_option_name the short version of the command line option
1265 that will be removed (including '-').
1266
1267 @param takes_arg True if the option takes an argument.
1268
Todd Fiala68615ce2015-09-15 21:38:04 +00001269 """
Todd Fialaea736242015-09-23 15:21:28 +00001270 if long_option_name is not None:
1271 regex_string = "^" + long_option_name + "="
1272 long_regex = re.compile(regex_string)
1273 if short_option_name is not None:
1274 # Short options we only match the -X and assume
1275 # any arg is one command line argument jammed together.
1276 # i.e. -O--abc=1 is a single argument in the args list.
1277 # We don't handle -O --abc=1, as argparse doesn't handle
1278 # it, either.
1279 regex_string = "^" + short_option_name
1280 short_regex = re.compile(regex_string)
1281
1282 def remove_long_internal():
1283 """Removes one matching long option from args.
1284 @returns True if one was found and removed; False otherwise.
1285 """
1286 try:
1287 index = args.index(long_option_name)
1288 # Handle the exact match case.
1289 if takes_arg:
1290 removal_count = 2
1291 else:
1292 removal_count = 1
1293 del args[index:index+removal_count]
1294 return True
1295 except ValueError:
1296 # Thanks to argparse not handling options with known arguments
1297 # like other options parsing libraries (see
1298 # https://bugs.python.org/issue9334), we need to support the
1299 # --results-formatter-options={second-level-arguments} (note
1300 # the equal sign to fool the first-level arguments parser into
1301 # not treating the second-level arguments as first-level
1302 # options). We're certainly at risk of getting this wrong
1303 # since now we're forced into the business of trying to figure
1304 # out what is an argument (although I think this
1305 # implementation will suffice).
1306 for index in range(len(args)):
1307 match = long_regex.search(args[index])
1308 if match:
1309 del args[index]
1310 return True
1311 return False
1312
1313 def remove_short_internal():
1314 """Removes one matching short option from args.
1315 @returns True if one was found and removed; False otherwise.
1316 """
Todd Fiala68615ce2015-09-15 21:38:04 +00001317 for index in range(len(args)):
Todd Fialaea736242015-09-23 15:21:28 +00001318 match = short_regex.search(args[index])
Todd Fiala68615ce2015-09-15 21:38:04 +00001319 if match:
Todd Fiala68615ce2015-09-15 21:38:04 +00001320 del args[index]
Todd Fialaea736242015-09-23 15:21:28 +00001321 return True
1322 return False
Todd Fiala68615ce2015-09-15 21:38:04 +00001323
Todd Fialaea736242015-09-23 15:21:28 +00001324 removal_count = 0
1325 while long_option_name is not None and remove_long_internal():
1326 removal_count += 1
1327 while short_option_name is not None and remove_short_internal():
1328 removal_count += 1
1329 if removal_count == 0:
1330 raise Exception(
1331 "failed to find at least one of '{}', '{}' in options".format(
1332 long_option_name, short_option_name))
Todd Fiala68615ce2015-09-15 21:38:04 +00001333
1334
1335def adjust_inferior_options(dotest_argv):
1336 """Adjusts the commandline args array for inferiors.
1337
1338 This method adjusts the inferior dotest commandline options based
1339 on the parallel test runner's options. Some of the inferior options
1340 will need to change to properly handle aggregation functionality.
1341 """
1342 global dotest_options
1343
1344 # If we don't have a session directory, create one.
1345 if not dotest_options.s:
1346 # no session log directory, we need to add this to prevent
1347 # every dotest invocation from creating its own directory
1348 import datetime
1349 # The windows platforms don't like ':' in the pathname.
Zachary Turneraf383ff2015-10-27 22:33:47 +00001350 timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S")
Todd Fiala68615ce2015-09-15 21:38:04 +00001351 dotest_argv.append('-s')
1352 dotest_argv.append(timestamp_started)
1353 dotest_options.s = timestamp_started
1354
1355 # Adjust inferior results formatter options - if the parallel
1356 # test runner is collecting into the user-specified test results,
1357 # we'll have inferiors spawn with the --results-port option and
1358 # strip the original test runner options.
1359 if dotest_options.results_file is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001360 _remove_option(dotest_argv, "--results-file", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001361 if dotest_options.results_port is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001362 _remove_option(dotest_argv, "--results-port", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001363 if dotest_options.results_formatter is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001364 _remove_option(dotest_argv, "--results-formatter", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001365 if dotest_options.results_formatter_options is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001366 _remove_option(dotest_argv, "--results-formatter-option", "-O",
1367 True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001368
Todd Fialacee6a6a2015-11-09 18:51:04 +00001369 # Remove the --curses shortcut if specified.
1370 if dotest_options.curses:
1371 _remove_option(dotest_argv, "--curses", None, False)
1372
Todd Fiala33896a92015-09-18 21:01:13 +00001373 # Remove test runner name if present.
1374 if dotest_options.test_runner_name is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001375 _remove_option(dotest_argv, "--test-runner-name", None, True)
Todd Fiala33896a92015-09-18 21:01:13 +00001376
1377
Todd Fiala83c32e32015-09-22 21:19:40 +00001378def is_darwin_version_lower_than(target_version):
1379 """Checks that os is Darwin and version is lower than target_version.
1380
1381 @param target_version the StrictVersion indicating the version
1382 we're checking against.
1383
1384 @return True if the OS is Darwin (OS X) and the version number of
1385 the OS is less than target_version; False in all other cases.
1386 """
1387 if platform.system() != 'Darwin':
1388 # Can't be Darwin lower than a certain version.
1389 return False
1390
1391 system_version = distutils.version.StrictVersion(platform.mac_ver()[0])
Zachary Turnerbac6e4f2015-11-03 21:37:27 +00001392 return seven.cmp_(system_version, target_version) < 0
Todd Fiala83c32e32015-09-22 21:19:40 +00001393
1394
1395def default_test_runner_name(num_threads):
1396 """Returns the default test runner name for the configuration.
1397
1398 @param num_threads the number of threads/workers this test runner is
1399 supposed to use.
1400
1401 @return the test runner name that should be used by default when
1402 no test runner was explicitly called out on the command line.
1403 """
1404 if num_threads == 1:
1405 # Use the serial runner.
1406 test_runner_name = "serial"
1407 elif os.name == "nt":
Adrian McCarthy040b31d2015-10-12 14:46:57 +00001408 # On Windows, Python uses CRT with a low limit on the number of open
1409 # files. If you have a lot of cores, the threading-pool runner will
Zachary Turnerf0c3f682015-11-06 18:14:31 +00001410 # often fail because it exceeds that limit. It's not clear what the
1411 # right balance is, so until we can investigate it more deeply,
1412 # just use the one that works
1413 test_runner_name = "multiprocessing-pool"
Todd Fiala83c32e32015-09-22 21:19:40 +00001414 elif is_darwin_version_lower_than(
1415 distutils.version.StrictVersion("10.10.0")):
1416 # OS X versions before 10.10 appear to have an issue using
1417 # the threading test runner. Fall back to multiprocessing.
1418 # Supports Ctrl-C.
1419 test_runner_name = "multiprocessing"
1420 else:
1421 # For everyone else, use the ctrl-c-enabled threading support.
1422 # Should use fewer system resources than the multprocessing
1423 # variant.
1424 test_runner_name = "threading"
1425 return test_runner_name
1426
1427
Todd Fiala8cbeed32015-09-08 22:22:33 +00001428def main(print_details_on_success, num_threads, test_subdir,
Todd Fiala68615ce2015-09-15 21:38:04 +00001429 test_runner_name, results_formatter):
Todd Fialafed95662015-09-03 18:58:44 +00001430 """Run dotest.py in inferior mode in parallel.
1431
1432 @param print_details_on_success the parsed value of the output-on-success
1433 command line argument. When True, details of a successful dotest inferior
1434 are printed even when everything succeeds. The normal behavior is to
1435 not print any details when all the inferior tests pass.
1436
1437 @param num_threads the parsed value of the num-threads command line
1438 argument.
1439
1440 @param test_subdir optionally specifies a subdir to limit testing
1441 within. May be None if the entire test tree is to be used. This subdir
1442 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001443
1444 @param test_runner_name if specified, contains the test runner
1445 name which selects the strategy used to run the isolated and
1446 optionally concurrent test runner. Specify None to allow the
1447 system to choose the most appropriate test runner given desired
1448 thread count and OS type.
1449
Todd Fiala68615ce2015-09-15 21:38:04 +00001450 @param results_formatter if specified, provides the TestResultsFormatter
1451 instance that will format and output test result data from the
1452 side-channel test results. When specified, inferior dotest calls
1453 will send test results side-channel data over a socket to the parallel
1454 test runner, which will forward them on to results_formatter.
Todd Fialafed95662015-09-03 18:58:44 +00001455 """
1456
Todd Fiala1cc97b42015-09-21 05:42:26 +00001457 # Do not shut down on sighup.
Ying Chend93aa102015-09-23 21:53:18 +00001458 if hasattr(signal, 'SIGHUP'):
1459 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala1cc97b42015-09-21 05:42:26 +00001460
Todd Fialafed95662015-09-03 18:58:44 +00001461 dotest_argv = sys.argv[1:]
1462
Greg Claytonb0d148e2015-09-21 17:25:01 +00001463 global output_on_success, RESULTS_FORMATTER
Todd Fialafed95662015-09-03 18:58:44 +00001464 output_on_success = print_details_on_success
Todd Fiala68615ce2015-09-15 21:38:04 +00001465 RESULTS_FORMATTER = results_formatter
Todd Fialafed95662015-09-03 18:58:44 +00001466
Vince Harrond5fa1022015-05-10 15:24:12 +00001467 # We can't use sys.path[0] to determine the script directory
1468 # because it doesn't work under a debugger
Vince Harron8994fed2015-05-22 19:49:23 +00001469 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +00001470 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +00001471 dotest_options = dotest_args.parse_args(parser, dotest_argv)
1472
Todd Fiala68615ce2015-09-15 21:38:04 +00001473 adjust_inferior_options(dotest_argv)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001474
1475 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +00001476
Vince Harrone06a7a82015-05-12 23:12:19 +00001477 # The root directory was specified on the command line
Todd Fiala68615ce2015-09-15 21:38:04 +00001478 test_directory = os.path.dirname(os.path.realpath(__file__))
Todd Fialafed95662015-09-03 18:58:44 +00001479 if test_subdir and len(test_subdir) > 0:
1480 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +00001481 else:
Todd Fialafed95662015-09-03 18:58:44 +00001482 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +00001483
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001484 # clean core files in test tree from previous runs (Linux)
1485 cores = find('core.*', test_subdir)
1486 for core in cores:
1487 os.unlink(core)
1488
Daniel Maleab42556f2013-04-19 18:32:53 +00001489 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +00001490
1491 # Figure out which testrunner strategy we'll use.
1492 runner_strategies_by_name = get_test_runner_strategies(num_threads)
1493
1494 # If the user didn't specify a test runner strategy, determine
1495 # the default now based on number of threads and OS type.
1496 if not test_runner_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001497 test_runner_name = default_test_runner_name(num_threads)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001498
1499 if test_runner_name not in runner_strategies_by_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001500 raise Exception(
1501 "specified testrunner name '{}' unknown. Valid choices: {}".format(
1502 test_runner_name,
Zachary Turner606e1e32015-10-23 17:53:51 +00001503 list(runner_strategies_by_name.keys())))
Todd Fiala8cbeed32015-09-08 22:22:33 +00001504 test_runner_func = runner_strategies_by_name[test_runner_name]
1505
1506 summary_results = walk_and_invoke(
Todd Fiala871b2e52015-09-22 22:47:34 +00001507 test_directory, test_subdir, dotest_argv,
1508 num_threads, 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 Harronf8b9a1d2015-05-18 19:40:54 +00001530 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
Vince Harron06381732015-05-12 23:10:36 +00001531 for xtime in expected_timeout:
1532 if xtime in timed_out:
1533 timed_out.remove(xtime)
1534 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001535 result = "ExpectedTimeout"
1536 elif xtime in passed:
1537 result = "UnexpectedCompletion"
1538 else:
1539 result = None # failed
1540
1541 if result:
1542 test_name = os.path.splitext(xtime)[0]
1543 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +00001544
Todd Fiala46a4e342015-12-02 18:48:38 +00001545 # Only run the old summary logic if we don't have a results formatter
1546 # that already prints the summary.
1547 if results_formatter is None or not results_formatter.replaces_summary():
1548 print_legacy_summary = True
1549 else:
1550 print_legacy_summary = False
Zachary Turner4cceca72015-08-14 16:45:32 +00001551
Todd Fiala46a4e342015-12-02 18:48:38 +00001552 if not print_legacy_summary:
Todd Fiala46a4e342015-12-02 18:48:38 +00001553 # Figure out exit code by count of test result types.
1554 issue_count = (
1555 results_formatter.counts_by_test_result_status(
Todd Fiala51831472015-12-09 06:45:43 +00001556 EventBuilder.STATUS_ERROR) +
Todd Fiala46a4e342015-12-02 18:48:38 +00001557 results_formatter.counts_by_test_result_status(
Todd Fiala51831472015-12-09 06:45:43 +00001558 EventBuilder.STATUS_FAILURE) +
1559 results_formatter.counts_by_test_result_status(
1560 EventBuilder.STATUS_TIMEOUT)
1561 )
1562
Todd Fiala46a4e342015-12-02 18:48:38 +00001563 # Return with appropriate result code
1564 if issue_count > 0:
1565 sys.exit(1)
1566 else:
1567 sys.exit(0)
1568 else:
1569 # Print the legacy test results summary.
1570 print()
1571 sys.stdout.write("Ran %d test suites" % num_test_files)
1572 if num_test_files > 0:
1573 sys.stdout.write(" (%d failed) (%f%%)" % (
1574 len(failed), 100.0 * len(failed) / num_test_files))
1575 print()
1576 sys.stdout.write("Ran %d test cases" % num_test_cases)
1577 if num_test_cases > 0:
1578 sys.stdout.write(" (%d failed) (%f%%)" % (
1579 fail_count, 100.0 * fail_count / num_test_cases))
1580 print()
1581 exit_code = 0
1582
1583 if len(failed) > 0:
1584 failed.sort()
1585 print("Failing Tests (%d)" % len(failed))
1586 for f in failed:
1587 print("%s: LLDB (suite) :: %s (%s)" % (
1588 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
1589 ))
1590 exit_code = 1
1591
1592 if len(unexpected_successes) > 0:
1593 unexpected_successes.sort()
1594 print("\nUnexpected Successes (%d)" % len(unexpected_successes))
1595 for u in unexpected_successes:
1596 print("UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info))
Zachary Turner4cceca72015-08-14 16:45:32 +00001597
1598 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001599
1600if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +00001601 sys.stderr.write(
1602 "error: dosep.py no longer supports being called directly. "
1603 "Please call dotest.py directly. The dosep.py-specific arguments "
1604 "have been added under the Parallel processing arguments.\n")
1605 sys.exit(128)