blob: 7e259bc3652fe388add062b73f1741d86d6ab09b [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
Todd Fiala685a7572015-12-14 21:28:46 +000052import lldbsuite
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000053import lldbsuite.support.seven as seven
54
Todd Fiala685a7572015-12-14 21:28:46 +000055from . import configuration
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000056from . import dotest_channels
57from . import dotest_args
Zachary Turner905a9882015-12-07 21:23:41 +000058from . import result_formatter
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000059
Todd Fiala51831472015-12-09 06:45:43 +000060from .result_formatter import EventBuilder
61
62
63# Todo: Convert this folder layout to be relative-import friendly and
64# don't hack up sys.path like this
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000065sys.path.append(os.path.join(os.path.dirname(__file__), "test_runner", "lib"))
Todd Fiala2d3754d2015-09-29 22:19:06 +000066import lldb_utils
67import process_control
Vince Harron17f429f2014-12-13 00:08:19 +000068
Vince Harron17f429f2014-12-13 00:08:19 +000069# Status codes for running command with timeout.
70eTimedOut, ePassed, eFailed = 124, 0, 1
71
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000072output_lock = None
73test_counter = None
74total_tests = None
Chaoren Linffc63b02015-08-12 18:02:49 +000075test_name_len = None
Pavel Labath05ab2372015-07-06 15:57:52 +000076dotest_options = None
Todd Fiala68615ce2015-09-15 21:38:04 +000077RESULTS_FORMATTER = None
78RUNNER_PROCESS_ASYNC_MAP = None
79RESULTS_LISTENER_CHANNEL = None
Chaoren Linb6325d02015-08-12 18:02:54 +000080
Todd Fiala33896a92015-09-18 21:01:13 +000081"""Contains an optional function pointer that can return the worker index
82 for the given thread/process calling it. Returns a 0-based index."""
83GET_WORKER_INDEX = None
84
Todd Fiala1cc97b42015-09-21 05:42:26 +000085
Todd Fiala33896a92015-09-18 21:01:13 +000086def setup_global_variables(
87 lock, counter, total, name_len, options, worker_index_map):
Chaoren Linffc63b02015-08-12 18:02:49 +000088 global output_lock, test_counter, total_tests, test_name_len
89 global dotest_options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000090 output_lock = lock
91 test_counter = counter
92 total_tests = total
Chaoren Linffc63b02015-08-12 18:02:49 +000093 test_name_len = name_len
Pavel Labath05ab2372015-07-06 15:57:52 +000094 dotest_options = options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000095
Todd Fiala33896a92015-09-18 21:01:13 +000096 if worker_index_map is not None:
97 # We'll use the output lock for this to avoid sharing another lock.
98 # This won't be used much.
99 index_lock = lock
100
101 def get_worker_index_use_pid():
102 """Returns a 0-based, process-unique index for the worker."""
103 pid = os.getpid()
104 with index_lock:
105 if pid not in worker_index_map:
106 worker_index_map[pid] = len(worker_index_map)
107 return worker_index_map[pid]
108
109 global GET_WORKER_INDEX
110 GET_WORKER_INDEX = get_worker_index_use_pid
111
Zachary Turner38e64172015-08-10 17:46:11 +0000112def report_test_failure(name, command, output):
113 global output_lock
114 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000115 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
Zachary Turnerff890da2015-10-19 23:45:41 +0000116 print(file=sys.stderr)
117 print(output, file=sys.stderr)
118 print("[%s FAILED]" % name, file=sys.stderr)
119 print("Command invoked: %s" % ' '.join(command), file=sys.stderr)
Chaoren Linffc63b02015-08-12 18:02:49 +0000120 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000121
Chaoren Linb6325d02015-08-12 18:02:54 +0000122
Zachary Turner38e64172015-08-10 17:46:11 +0000123def report_test_pass(name, output):
Zachary Turner80310c22015-12-10 18:51:02 +0000124 global output_lock
Zachary Turner38e64172015-08-10 17:46:11 +0000125 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000126 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000127
Chaoren Linb6325d02015-08-12 18:02:54 +0000128
Chaoren Linffc63b02015-08-12 18:02:49 +0000129def update_progress(test_name=""):
130 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000131 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000132 counter_len = len(str(total_tests))
Greg Clayton1827fc22015-09-19 00:39:09 +0000133 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
134 sys.stderr.write(
135 "\r%*d out of %d test suites processed - %-*s" %
136 (counter_len, test_counter.value, total_tests,
137 test_name_len.value, test_name))
Chaoren Linffc63b02015-08-12 18:02:49 +0000138 if len(test_name) > test_name_len.value:
139 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000140 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000141 sys.stdout.flush()
142 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000143
Chaoren Linb6325d02015-08-12 18:02:54 +0000144
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000145def parse_test_results(output):
146 passes = 0
147 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000148 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000149 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000150 pass_count = re.search("^RESULT:.*([0-9]+) passes",
151 result, re.MULTILINE)
152 fail_count = re.search("^RESULT:.*([0-9]+) failures",
153 result, re.MULTILINE)
154 error_count = re.search("^RESULT:.*([0-9]+) errors",
155 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000156 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
157 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000158 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000159 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000160 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000161 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000162 if unexpected_success_count is not None:
163 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000164 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000165 failures = failures + int(error_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000166 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000167
Chaoren Linb6325d02015-08-12 18:02:54 +0000168
Todd Fiala2d3754d2015-09-29 22:19:06 +0000169class DoTestProcessDriver(process_control.ProcessDriver):
170 """Drives the dotest.py inferior process and handles bookkeeping."""
171 def __init__(self, output_file, output_file_lock, pid_events, file_name,
172 soft_terminate_timeout):
173 super(DoTestProcessDriver, self).__init__(
174 soft_terminate_timeout=soft_terminate_timeout)
175 self.output_file = output_file
176 self.output_lock = lldb_utils.OptionalWith(output_file_lock)
177 self.pid_events = pid_events
178 self.results = None
179 self.file_name = file_name
180
181 def write(self, content):
182 with self.output_lock:
183 self.output_file.write(content)
184
185 def on_process_started(self):
186 if self.pid_events:
187 self.pid_events.put_nowait(('created', self.process.pid))
188
189 def on_process_exited(self, command, output, was_timeout, exit_status):
190 if self.pid_events:
191 # No point in culling out those with no exit_status (i.e.
192 # those we failed to kill). That would just cause
193 # downstream code to try to kill it later on a Ctrl-C. At
194 # this point, a best-effort-to-kill already took place. So
195 # call it destroyed here.
196 self.pid_events.put_nowait(('destroyed', self.process.pid))
197
198 # Override the exit status if it was a timeout.
199 if was_timeout:
200 exit_status = eTimedOut
201
202 # If we didn't end up with any output, call it empty for
203 # stdout/stderr.
204 if output is None:
205 output = ('', '')
206
207 # Now parse the output.
208 passes, failures, unexpected_successes = parse_test_results(output)
209 if exit_status == 0:
210 # stdout does not have any useful information from 'dotest.py',
211 # only stderr does.
212 report_test_pass(self.file_name, output[1])
213 else:
214 report_test_failure(self.file_name, command, output[1])
215
216 # Save off the results for the caller.
217 self.results = (
218 self.file_name,
219 exit_status,
220 passes,
221 failures,
222 unexpected_successes)
223
Todd Fiala51831472015-12-09 06:45:43 +0000224 def is_exceptional_exit(self):
225 """Returns whether the process returned a timeout.
226
227 Not valid to call until after on_process_exited() completes.
228
229 @return True if the exit is an exceptional exit (e.g. signal on
230 POSIX); False otherwise.
231 """
232 if self.results is None:
233 raise Exception(
234 "exit status checked before results are available")
235 return self.process_helper.is_exceptional_exit(
236 self.results[1])
237
238 def exceptional_exit_details(self):
239 if self.results is None:
240 raise Exception(
241 "exit status checked before results are available")
242 return self.process_helper.exceptional_exit_details(self.results[1])
243
244 def is_timeout(self):
245 if self.results is None:
246 raise Exception(
247 "exit status checked before results are available")
248 return self.results[1] == eTimedOut
249
Todd Fiala2d3754d2015-09-29 22:19:06 +0000250
251def get_soft_terminate_timeout():
252 # Defaults to 10 seconds, but can set
253 # LLDB_TEST_SOFT_TERMINATE_TIMEOUT to a floating point
254 # number in seconds. This value indicates how long
255 # the test runner will wait for the dotest inferior to
256 # handle a timeout via a soft terminate before it will
257 # assume that failed and do a hard terminate.
258
259 # TODO plumb through command-line option
260 return float(os.environ.get('LLDB_TEST_SOFT_TERMINATE_TIMEOUT', 10.0))
261
262
263def want_core_on_soft_terminate():
264 # TODO plumb through command-line option
265 if platform.system() == 'Linux':
266 return True
267 else:
268 return False
Todd Fialada817b62015-09-22 18:05:11 +0000269
270
Todd Fiala51831472015-12-09 06:45:43 +0000271def send_events_to_collector(events, command):
272 """Sends the given events to the collector described in the command line.
273
274 @param events the list of events to send to the test event collector.
275 @param command the inferior command line which contains the details on
276 how to connect to the test event collector.
277 """
278 if events is None or len(events) == 0:
279 # Nothing to do.
280 return
281
282 # Find the port we need to connect to from the --results-port option.
283 try:
284 arg_index = command.index("--results-port") + 1
285 except ValueError:
286 # There is no results port, so no way to communicate back to
287 # the event collector. This is not a problem if we're not
288 # using event aggregation.
289 # TODO flag as error once we always use the event system
290 print(
291 "INFO: no event collector, skipping post-inferior test "
292 "event reporting")
293 return
294
295 if arg_index >= len(command):
296 raise Exception(
297 "expected collector port at index {} in {}".format(
298 arg_index, command))
299 event_port = int(command[arg_index])
300
301 # Create results formatter connected back to collector via socket.
302 config = result_formatter.FormatterConfig()
303 config.port = event_port
304 formatter_spec = result_formatter.create_results_formatter(config)
305 if formatter_spec is None or formatter_spec.formatter is None:
306 raise Exception(
307 "Failed to create socket-based ResultsFormatter "
308 "back to test event collector")
309
310 # Send the events: the port-based event just pickles the content
311 # and sends over to the server side of the socket.
312 for event in events:
313 formatter_spec.formatter.handle_event(event)
314
315 # Cleanup
316 if formatter_spec.cleanup_func is not None:
317 formatter_spec.cleanup_func()
318
319
Todd Fiala93153922015-12-12 19:26:56 +0000320def send_inferior_post_run_events(
321 command, worker_index, process_driver, test_filename):
Todd Fiala51831472015-12-09 06:45:43 +0000322 """Sends any test events that should be generated after the inferior runs.
323
324 These events would include timeouts and exceptional (i.e. signal-returning)
325 process completion results.
326
327 @param command the list of command parameters passed to subprocess.Popen().
328 @param worker_index the worker index (possibly None) used to run
329 this process
330 @param process_driver the ProcessDriver-derived instance that was used
331 to run the inferior process.
Todd Fiala93153922015-12-12 19:26:56 +0000332 @param test_filename the full path to the Python test file that is being
333 run.
Todd Fiala51831472015-12-09 06:45:43 +0000334 """
335 if process_driver is None:
336 raise Exception("process_driver must not be None")
337 if process_driver.results is None:
338 # Invalid condition - the results should have been set one way or
339 # another, even in a timeout.
340 raise Exception("process_driver.results were not set")
341
342 # The code below fills in the post events struct. If there are any post
343 # events to fire up, we'll try to make a connection to the socket and
344 # provide the results.
345 post_events = []
346
347 # Handle signal/exceptional exits.
348 if process_driver.is_exceptional_exit():
349 (code, desc) = process_driver.exceptional_exit_details()
Todd Fiala51831472015-12-09 06:45:43 +0000350 post_events.append(
351 EventBuilder.event_for_job_exceptional_exit(
352 process_driver.pid,
353 worker_index,
354 code,
355 desc,
356 test_filename,
357 command))
358
359 # Handle timeouts.
360 if process_driver.is_timeout():
Todd Fiala51831472015-12-09 06:45:43 +0000361 post_events.append(EventBuilder.event_for_job_timeout(
362 process_driver.pid,
363 worker_index,
364 test_filename,
365 command))
366
367 if len(post_events) > 0:
368 send_events_to_collector(post_events, command)
369
370
Todd Fiala93153922015-12-12 19:26:56 +0000371def call_with_timeout(
372 command, timeout, name, inferior_pid_events, test_filename):
Todd Fiala2d3754d2015-09-29 22:19:06 +0000373 # Add our worker index (if we have one) to all test events
374 # from this inferior.
Todd Fiala51831472015-12-09 06:45:43 +0000375 worker_index = None
Todd Fiala33896a92015-09-18 21:01:13 +0000376 if GET_WORKER_INDEX is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000377 try:
378 worker_index = GET_WORKER_INDEX()
379 command.extend([
Todd Fiala2d3754d2015-09-29 22:19:06 +0000380 "--event-add-entries",
381 "worker_index={}:int".format(worker_index)])
382 except: # pylint: disable=bare-except
383 # Ctrl-C does bad things to multiprocessing.Manager.dict()
384 # lookup. Just swallow it.
Todd Fialae83f1402015-09-18 22:45:31 +0000385 pass
386
Todd Fiala2d3754d2015-09-29 22:19:06 +0000387 # Create the inferior dotest.py ProcessDriver.
388 soft_terminate_timeout = get_soft_terminate_timeout()
389 want_core = want_core_on_soft_terminate()
Todd Fiala68615ce2015-09-15 21:38:04 +0000390
Todd Fiala2d3754d2015-09-29 22:19:06 +0000391 process_driver = DoTestProcessDriver(
392 sys.stdout,
393 output_lock,
394 inferior_pid_events,
395 name,
396 soft_terminate_timeout)
Todd Fiala68615ce2015-09-15 21:38:04 +0000397
Todd Fiala2d3754d2015-09-29 22:19:06 +0000398 # Run it with a timeout.
399 process_driver.run_command_with_timeout(command, timeout, want_core)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000400
Todd Fiala2d3754d2015-09-29 22:19:06 +0000401 # Return the results.
402 if not process_driver.results:
403 # This is truly exceptional. Even a failing or timed out
404 # binary should have called the results-generation code.
405 raise Exception("no test results were generated whatsoever")
Todd Fiala51831472015-12-09 06:45:43 +0000406
407 # Handle cases where the test inferior cannot adequately provide
408 # meaningful results to the test event system.
409 send_inferior_post_run_events(
410 command,
411 worker_index,
Todd Fiala93153922015-12-12 19:26:56 +0000412 process_driver,
413 test_filename)
Todd Fiala51831472015-12-09 06:45:43 +0000414
Todd Fiala2d3754d2015-09-29 22:19:06 +0000415 return process_driver.results
Johnny Chene8d9dc62011-10-31 19:04:07 +0000416
Chaoren Linb6325d02015-08-12 18:02:54 +0000417
Zachary Turner7d564542015-11-02 19:19:49 +0000418def process_dir(root, files, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000419 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000420 results = []
Todd Fiala685a7572015-12-14 21:28:46 +0000421 for (base_name, full_test_path) in files:
Zachary Turner7d564542015-11-02 19:19:49 +0000422 import __main__ as main
423 script_file = main.__file__
Zachary Turnerf6896b02015-01-05 19:37:03 +0000424 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000425 dotest_argv +
Todd Fiala685a7572015-12-14 21:28:46 +0000426 ["--inferior", "-p", base_name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000427
Todd Fiala685a7572015-12-14 21:28:46 +0000428 timeout_name = os.path.basename(os.path.splitext(base_name)[0]).upper()
Vince Harron17f429f2014-12-13 00:08:19 +0000429
Chaoren Linb6325d02015-08-12 18:02:54 +0000430 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
431 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000432
Todd Fiala8cbeed32015-09-08 22:22:33 +0000433 results.append(call_with_timeout(
Todd Fiala685a7572015-12-14 21:28:46 +0000434 command, timeout, base_name, inferior_pid_events, full_test_path))
Vince Harron17f429f2014-12-13 00:08:19 +0000435
Zachary Turner4cceca72015-08-14 16:45:32 +0000436 # result = (name, status, passes, failures, unexpected_successes)
437 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000438 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000439 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000440 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000441 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000442 if status != ePassed]
Todd Fiala93153922015-12-12 19:26:56 +0000443 unexpected_passes = [
444 name for name, _, _, _, unexpected_successes in results
445 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000446
Chaoren Line80372a2015-08-12 18:02:53 +0000447 pass_count = sum([result[2] for result in results])
448 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000449
Todd Fiala93153922015-12-12 19:26:56 +0000450 return (
451 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
Todd Fiala685a7572015-12-14 21:28:46 +0000619 tests = [
620 (filename, os.path.join(root, filename))
621 for filename in files
622 if is_test_filename(root, filename)]
Todd Fiala8cbeed32015-09-08 22:22:33 +0000623 if tests:
624 found_func(root, tests)
625
626
627def initialize_global_vars_common(num_threads, test_work_items):
628 global total_tests, test_counter, test_name_len
Todd Fiala51831472015-12-09 06:45:43 +0000629
Todd Fiala8cbeed32015-09-08 22:22:33 +0000630 total_tests = sum([len(item[1]) for item in test_work_items])
631 test_counter = multiprocessing.Value('i', 0)
632 test_name_len = multiprocessing.Value('i', 0)
Greg Clayton1827fc22015-09-19 00:39:09 +0000633 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
Zachary Turnerff890da2015-10-19 23:45:41 +0000634 print("Testing: %d test suites, %d thread%s" % (
635 total_tests, num_threads, (num_threads > 1) * "s"), file=sys.stderr)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000636 update_progress()
637
638
639def initialize_global_vars_multiprocessing(num_threads, test_work_items):
640 # Initialize the global state we'll use to communicate with the
641 # rest of the flat module.
642 global output_lock
643 output_lock = multiprocessing.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000644
Todd Fiala8cbeed32015-09-08 22:22:33 +0000645 initialize_global_vars_common(num_threads, test_work_items)
646
647
648def initialize_global_vars_threading(num_threads, test_work_items):
Todd Fiala33896a92015-09-18 21:01:13 +0000649 """Initializes global variables used in threading mode.
650 @param num_threads specifies the number of workers used.
651 @param test_work_items specifies all the work items
652 that will be processed.
653 """
Todd Fiala8cbeed32015-09-08 22:22:33 +0000654 # Initialize the global state we'll use to communicate with the
655 # rest of the flat module.
656 global output_lock
657 output_lock = threading.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000658
659 index_lock = threading.RLock()
660 index_map = {}
661
662 def get_worker_index_threading():
663 """Returns a 0-based, thread-unique index for the worker thread."""
664 thread_id = threading.current_thread().ident
665 with index_lock:
666 if thread_id not in index_map:
667 index_map[thread_id] = len(index_map)
668 return index_map[thread_id]
669
670
671 global GET_WORKER_INDEX
672 GET_WORKER_INDEX = get_worker_index_threading
673
Todd Fiala8cbeed32015-09-08 22:22:33 +0000674 initialize_global_vars_common(num_threads, test_work_items)
675
676
Todd Fiala68615ce2015-09-15 21:38:04 +0000677def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler):
678 """Provides a main loop that is Ctrl-C protected.
679
680 The main loop calls the main_op_func() repeatedly until done_func()
681 returns true. The ctrl_c_handler() method is called with a single
682 int parameter that contains the number of times the ctrl_c has been
683 hit (starting with 1). The ctrl_c_handler() should mutate whatever
684 it needs to have the done_func() return True as soon as it is desired
685 to exit the loop.
686 """
687 done = False
688 ctrl_c_count = 0
689
690 while not done:
691 try:
692 # See if we're done. Start with done check since it is
693 # the first thing executed after a Ctrl-C handler in the
694 # following loop.
695 done = done_func()
696 if not done:
697 # Run the main op once.
698 main_op_func()
699
700 except KeyboardInterrupt:
701 ctrl_c_count += 1
702 ctrl_c_handler(ctrl_c_count)
703
704
705def pump_workers_and_asyncore_map(workers, asyncore_map):
706 """Prunes out completed workers and maintains the asyncore loop.
707
708 The asyncore loop contains the optional socket listener
709 and handlers. When all workers are complete, this method
710 takes care of stopping the listener. It also runs the
711 asyncore loop for the given async map for 10 iterations.
712
713 @param workers the list of worker Thread/Process instances.
714
715 @param asyncore_map the asyncore threading-aware map that
716 indicates which channels are in use and still alive.
717 """
718
719 # Check on all the workers, removing them from the workers
720 # list as they complete.
721 dead_workers = []
722 for worker in workers:
723 # This non-blocking join call is what allows us
724 # to still receive keyboard interrupts.
725 worker.join(0.01)
726 if not worker.is_alive():
727 dead_workers.append(worker)
728 # Clear out the completed workers
729 for dead_worker in dead_workers:
730 workers.remove(dead_worker)
731
732 # If there are no more workers and there is a listener,
733 # close the listener.
734 global RESULTS_LISTENER_CHANNEL
735 if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None:
736 RESULTS_LISTENER_CHANNEL.close()
737 RESULTS_LISTENER_CHANNEL = None
738
739 # Pump the asyncore map if it isn't empty.
740 if len(asyncore_map) > 0:
741 asyncore.loop(0.1, False, asyncore_map, 10)
742
743
744def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events,
745 stop_all_inferiors_func):
746 """Performs the appropriate ctrl-c action for non-pool parallel test runners
747
748 @param ctrl_c_count starting with 1, indicates the number of times ctrl-c
749 has been intercepted. The value is 1 on the first intercept, 2 on the
750 second, etc.
751
752 @param job_queue a Queue object that contains the work still outstanding
753 (i.e. hasn't been assigned to a worker yet).
754
755 @param workers list of Thread or Process workers.
756
757 @param inferior_pid_events specifies a Queue of inferior process
758 construction and destruction events. Used to build the list of inferior
759 processes that should be killed if we get that far.
760
761 @param stop_all_inferiors_func a callable object that takes the
762 workers and inferior_pid_events parameters (in that order) if a hard
763 stop is to be used on the workers.
764 """
765
766 # Print out which Ctrl-C we're handling.
767 key_name = [
768 "first",
769 "second",
770 "third",
771 "many"]
772
773 if ctrl_c_count < len(key_name):
774 name_index = ctrl_c_count - 1
775 else:
776 name_index = len(key_name) - 1
777 message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index])
778 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000779 print(message)
Todd Fiala68615ce2015-09-15 21:38:04 +0000780
781 if ctrl_c_count == 1:
782 # Remove all outstanding items from the work queue so we stop
783 # doing any more new work.
784 while not job_queue.empty():
785 try:
786 # Just drain it to stop more work from being started.
787 job_queue.get_nowait()
Zachary Turner814236d2015-10-21 17:48:52 +0000788 except queue.Empty:
Todd Fiala68615ce2015-09-15 21:38:04 +0000789 pass
790 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000791 print("Stopped more work from being started.")
Todd Fiala68615ce2015-09-15 21:38:04 +0000792 elif ctrl_c_count == 2:
793 # Try to stop all inferiors, even the ones currently doing work.
794 stop_all_inferiors_func(workers, inferior_pid_events)
795 else:
796 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000797 print("All teardown activities kicked off, should finish soon.")
Todd Fiala68615ce2015-09-15 21:38:04 +0000798
799
800def workers_and_async_done(workers, async_map):
801 """Returns True if the workers list and asyncore channels are all done.
802
803 @param workers list of workers (threads/processes). These must adhere
804 to the threading Thread or multiprocessing.Process interface.
805
806 @param async_map the threading-aware asyncore channel map to check
807 for live channels.
808
809 @return False if the workers list exists and has any entries in it, or
810 if the async_map exists and has any entries left in it; otherwise, True.
811 """
812 if workers is not None and len(workers) > 0:
813 # We're not done if we still have workers left.
814 return False
815 if async_map is not None and len(async_map) > 0:
816 return False
817 # We're done.
818 return True
819
820
Todd Fiala8cbeed32015-09-08 22:22:33 +0000821def multiprocessing_test_runner(num_threads, test_work_items):
822 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
823
824 This concurrent test runner is based on the multiprocessing
825 library, and rolls its own worker pooling strategy so it
826 can handle Ctrl-C properly.
827
828 This test runner is known to have an issue running on
829 Windows platforms.
830
831 @param num_threads the number of worker processes to use.
832
833 @param test_work_items the iterable of test work item tuples
834 to run.
835 """
836
837 # Initialize our global state.
838 initialize_global_vars_multiprocessing(num_threads, test_work_items)
839
840 # Create jobs.
841 job_queue = multiprocessing.Queue(len(test_work_items))
842 for test_work_item in test_work_items:
843 job_queue.put(test_work_item)
844
845 result_queue = multiprocessing.Queue(len(test_work_items))
846
847 # Create queues for started child pids. Terminating
848 # the multiprocess processes does not terminate the
849 # child processes they spawn. We can remove this tracking
850 # if/when we move to having the multiprocess process directly
851 # perform the test logic. The Queue size needs to be able to
852 # hold 2 * (num inferior dotest.py processes started) entries.
853 inferior_pid_events = multiprocessing.Queue(4096)
854
Todd Fiala33896a92015-09-18 21:01:13 +0000855 # Worker dictionary allows each worker to figure out its worker index.
856 manager = multiprocessing.Manager()
857 worker_index_map = manager.dict()
858
Todd Fiala8cbeed32015-09-08 22:22:33 +0000859 # Create workers. We don't use multiprocessing.Pool due to
860 # challenges with handling ^C keyboard interrupts.
861 workers = []
862 for _ in range(num_threads):
863 worker = multiprocessing.Process(
864 target=process_dir_worker_multiprocessing,
865 args=(output_lock,
866 test_counter,
867 total_tests,
868 test_name_len,
869 dotest_options,
870 job_queue,
871 result_queue,
Todd Fiala33896a92015-09-18 21:01:13 +0000872 inferior_pid_events,
873 worker_index_map))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000874 worker.start()
875 workers.append(worker)
876
Todd Fiala68615ce2015-09-15 21:38:04 +0000877 # Main loop: wait for all workers to finish and wait for
878 # the socket handlers to wrap up.
879 ctrl_c_loop(
880 # Main operation of loop
881 lambda: pump_workers_and_asyncore_map(
882 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000883
Todd Fiala68615ce2015-09-15 21:38:04 +0000884 # Return True when we're done with the main loop.
885 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000886
Todd Fiala68615ce2015-09-15 21:38:04 +0000887 # Indicate what we do when we receive one or more Ctrl-Cs.
888 lambda ctrl_c_count: handle_ctrl_c(
889 ctrl_c_count, job_queue, workers, inferior_pid_events,
890 kill_all_worker_processes))
891
892 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000893 test_results = []
894 while not result_queue.empty():
895 test_results.append(result_queue.get(block=False))
896 return test_results
897
898
Todd Fiala68615ce2015-09-15 21:38:04 +0000899def map_async_run_loop(future, channel_map, listener_channel):
900 """Blocks until the Pool.map_async completes and the channel completes.
901
902 @param future an AsyncResult instance from a Pool.map_async() call.
903
904 @param channel_map the asyncore dispatch channel map that should be pumped.
905 Optional: may be None.
906
907 @param listener_channel the channel representing a listener that should be
908 closed once the map_async results are available.
909
910 @return the results from the async_result instance.
911 """
912 map_results = None
913
914 done = False
915 while not done:
916 # Check if we need to reap the map results.
917 if map_results is None:
918 if future.ready():
919 # Get the results.
920 map_results = future.get()
921
922 # Close the runner process listener channel if we have
923 # one: no more connections will be incoming.
924 if listener_channel is not None:
925 listener_channel.close()
926
927 # Pump the asyncore loop if we have a listener socket.
928 if channel_map is not None:
929 asyncore.loop(0.01, False, channel_map, 10)
930
931 # Figure out if we're done running.
932 done = map_results is not None
933 if channel_map is not None:
934 # We have a runner process async map. Check if it
935 # is complete.
936 if len(channel_map) > 0:
937 # We still have an asyncore channel running. Not done yet.
938 done = False
939
940 return map_results
941
942
Todd Fiala8cbeed32015-09-08 22:22:33 +0000943def multiprocessing_test_runner_pool(num_threads, test_work_items):
944 # Initialize our global state.
945 initialize_global_vars_multiprocessing(num_threads, test_work_items)
946
Todd Fiala33896a92015-09-18 21:01:13 +0000947 manager = multiprocessing.Manager()
948 worker_index_map = manager.dict()
949
Todd Fiala8cbeed32015-09-08 22:22:33 +0000950 pool = multiprocessing.Pool(
951 num_threads,
952 initializer=setup_global_variables,
953 initargs=(output_lock, test_counter, total_tests, test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000954 dotest_options, worker_index_map))
Todd Fiala68615ce2015-09-15 21:38:04 +0000955
956 # Start the map operation (async mode).
957 map_future = pool.map_async(
958 process_dir_worker_multiprocessing_pool, test_work_items)
959 return map_async_run_loop(
960 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000961
962
963def threading_test_runner(num_threads, test_work_items):
964 """Provides hand-wrapped pooling threading-based test runner adapter
965 with Ctrl-C support.
966
967 This concurrent test runner is based on the threading
968 library, and rolls its own worker pooling strategy so it
969 can handle Ctrl-C properly.
970
971 @param num_threads the number of worker processes to use.
972
973 @param test_work_items the iterable of test work item tuples
974 to run.
975 """
976
977 # Initialize our global state.
978 initialize_global_vars_threading(num_threads, test_work_items)
979
980 # Create jobs.
Zachary Turner814236d2015-10-21 17:48:52 +0000981 job_queue = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000982 for test_work_item in test_work_items:
983 job_queue.put(test_work_item)
984
Zachary Turner814236d2015-10-21 17:48:52 +0000985 result_queue = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000986
987 # Create queues for started child pids. Terminating
988 # the threading threads does not terminate the
989 # child processes they spawn.
Zachary Turner814236d2015-10-21 17:48:52 +0000990 inferior_pid_events = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000991
992 # Create workers. We don't use multiprocessing.pool.ThreadedPool
993 # due to challenges with handling ^C keyboard interrupts.
994 workers = []
995 for _ in range(num_threads):
996 worker = threading.Thread(
997 target=process_dir_worker_threading,
Todd Fiala68615ce2015-09-15 21:38:04 +0000998 args=(job_queue,
Todd Fiala8cbeed32015-09-08 22:22:33 +0000999 result_queue,
1000 inferior_pid_events))
1001 worker.start()
1002 workers.append(worker)
1003
Todd Fiala68615ce2015-09-15 21:38:04 +00001004 # Main loop: wait for all workers to finish and wait for
1005 # the socket handlers to wrap up.
1006 ctrl_c_loop(
1007 # Main operation of loop
1008 lambda: pump_workers_and_asyncore_map(
1009 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +00001010
Todd Fiala68615ce2015-09-15 21:38:04 +00001011 # Return True when we're done with the main loop.
1012 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +00001013
Todd Fiala68615ce2015-09-15 21:38:04 +00001014 # Indicate what we do when we receive one or more Ctrl-Cs.
1015 lambda ctrl_c_count: handle_ctrl_c(
1016 ctrl_c_count, job_queue, workers, inferior_pid_events,
1017 kill_all_worker_threads))
Todd Fiala8cbeed32015-09-08 22:22:33 +00001018
Todd Fiala68615ce2015-09-15 21:38:04 +00001019 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001020 test_results = []
1021 while not result_queue.empty():
1022 test_results.append(result_queue.get(block=False))
1023 return test_results
1024
1025
1026def threading_test_runner_pool(num_threads, test_work_items):
1027 # Initialize our global state.
1028 initialize_global_vars_threading(num_threads, test_work_items)
1029
Todd Fiala68615ce2015-09-15 21:38:04 +00001030 pool = multiprocessing.pool.ThreadPool(num_threads)
1031 map_future = pool.map_async(
1032 process_dir_worker_threading_pool, test_work_items)
1033
1034 return map_async_run_loop(
1035 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
1036
1037
1038def asyncore_run_loop(channel_map):
1039 try:
1040 asyncore.loop(None, False, channel_map)
1041 except:
1042 # Swallow it, we're seeing:
1043 # error: (9, 'Bad file descriptor')
1044 # when the listener channel is closed. Shouldn't be the case.
1045 pass
Todd Fiala8cbeed32015-09-08 22:22:33 +00001046
1047
1048def inprocess_exec_test_runner(test_work_items):
1049 # Initialize our global state.
1050 initialize_global_vars_multiprocessing(1, test_work_items)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001051
Todd Fiala33896a92015-09-18 21:01:13 +00001052 # We're always worker index 0
1053 global GET_WORKER_INDEX
1054 GET_WORKER_INDEX = lambda: 0
1055
Todd Fiala68615ce2015-09-15 21:38:04 +00001056 # Run the listener and related channel maps in a separate thread.
1057 # global RUNNER_PROCESS_ASYNC_MAP
1058 global RESULTS_LISTENER_CHANNEL
1059 if RESULTS_LISTENER_CHANNEL is not None:
1060 socket_thread = threading.Thread(
1061 target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP))
1062 socket_thread.start()
1063
1064 # Do the work.
Zachary Turner1c4059a2015-10-22 20:39:59 +00001065 test_results = list(map(process_dir_mapper_inprocess, test_work_items))
Todd Fiala68615ce2015-09-15 21:38:04 +00001066
1067 # If we have a listener channel, shut it down here.
1068 if RESULTS_LISTENER_CHANNEL is not None:
1069 # Close down the channel.
1070 RESULTS_LISTENER_CHANNEL.close()
1071 RESULTS_LISTENER_CHANNEL = None
1072
1073 # Wait for the listener and handlers to complete.
1074 socket_thread.join()
1075
1076 return test_results
Todd Fiala8cbeed32015-09-08 22:22:33 +00001077
Todd Fialad06a9c92015-12-12 00:34:57 +00001078def walk_and_invoke(test_files, dotest_argv, num_workers, test_runner_func):
1079 """Invokes the test runner on each test file specified by test_files.
Vince Harrone06a7a82015-05-12 23:12:19 +00001080
Todd Fialad06a9c92015-12-12 00:34:57 +00001081 @param test_files a list of (test_subdir, list_of_test_files_in_dir)
1082 @param num_workers the number of worker queues working on these test files
1083 @param test_runner_func the test runner configured to run the tests
1084
1085 @return a tuple of results from the running of the specified tests,
1086 of the form (timed_out, passed, failed, unexpected_successes, pass_count,
1087 fail_count)
Vince Harrone06a7a82015-05-12 23:12:19 +00001088 """
Todd Fiala68615ce2015-09-15 21:38:04 +00001089 # The async_map is important to keep all thread-related asyncore
1090 # channels distinct when we call asyncore.loop() later on.
1091 global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP
1092 RUNNER_PROCESS_ASYNC_MAP = {}
1093
1094 # If we're outputting side-channel test results, create the socket
1095 # listener channel and tell the inferior to send results to the
1096 # port on which we'll be listening.
1097 if RESULTS_FORMATTER is not None:
Todd Fialae83f1402015-09-18 22:45:31 +00001098 forwarding_func = RESULTS_FORMATTER.handle_event
Todd Fiala68615ce2015-09-15 21:38:04 +00001099 RESULTS_LISTENER_CHANNEL = (
1100 dotest_channels.UnpicklingForwardingListenerChannel(
Todd Fiala871b2e52015-09-22 22:47:34 +00001101 RUNNER_PROCESS_ASYNC_MAP, "localhost", 0,
1102 2 * num_workers, forwarding_func))
Todd Fiala685a7572015-12-14 21:28:46 +00001103 # Set the results port command line arg. Might have been
1104 # inserted previous, so first try to replace.
1105 listener_port = str(RESULTS_LISTENER_CHANNEL.address[1])
1106 try:
1107 port_value_index = dotest_argv.index("--results-port") + 1
1108 dotest_argv[port_value_index] = listener_port
1109 except ValueError:
1110 # --results-port doesn't exist (yet), add it
1111 dotest_argv.append("--results-port")
1112 dotest_argv.append(listener_port)
Todd Fiala3f0a3602014-07-08 06:42:37 +00001113
Todd Fialad06a9c92015-12-12 00:34:57 +00001114 # Build the test work items out of the (dir, file_list) entries passed in.
Todd Fiala3f0a3602014-07-08 06:42:37 +00001115 test_work_items = []
Todd Fialad06a9c92015-12-12 00:34:57 +00001116 for entry in test_files:
1117 test_work_items.append((entry[0], entry[1], dotest_argv, None))
Todd Fiala3f0a3602014-07-08 06:42:37 +00001118
Todd Fiala8cbeed32015-09-08 22:22:33 +00001119 # Convert test work items into test results using whatever
1120 # was provided as the test run function.
1121 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +00001122
Todd Fiala8cbeed32015-09-08 22:22:33 +00001123 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +00001124 timed_out = sum([result[0] for result in test_results], [])
1125 passed = sum([result[1] for result in test_results], [])
1126 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +00001127 unexpected_successes = sum([result[3] for result in test_results], [])
1128 pass_count = sum([result[4] for result in test_results])
1129 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +00001130
Todd Fiala8cbeed32015-09-08 22:22:33 +00001131 return (timed_out, passed, failed, unexpected_successes, pass_count,
1132 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001133
Chaoren Linb6325d02015-08-12 18:02:54 +00001134
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001135def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +00001136 # returns a set of test filenames that might timeout
1137 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +00001138 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001139 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +00001140 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001141 else:
Todd Fiala68615ce2015-09-15 21:38:04 +00001142 m = re.search(r'remote-(\w+)', platform_name)
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001143 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +00001144
1145 expected_timeout = set()
Todd Fialaa8fee7f2015-12-11 19:44:23 +00001146 expected_timeout.add("TestExpectedTimeout.py")
Vince Harron06381732015-05-12 23:10:36 +00001147
1148 if target.startswith("linux"):
1149 expected_timeout |= {
Vince Harron06381732015-05-12 23:10:36 +00001150 "TestConnectRemote.py",
1151 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +00001152 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +00001153 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001154
1155 # Times out in ~10% of the times on the build bot
1156 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +00001157 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001158 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +00001159 "TestThreadStepOut.py",
1160 }
1161 elif target.startswith("android"):
1162 expected_timeout |= {
1163 "TestExitDuringStep.py",
1164 "TestHelloWorld.py",
1165 }
Chaoren Linfebef1b2015-08-19 17:22:12 +00001166 if host.startswith("win32"):
1167 expected_timeout |= {
1168 "TestEvents.py",
1169 "TestThreadStates.py",
1170 }
Ed Maste4dd8fba2015-05-14 16:25:52 +00001171 elif target.startswith("freebsd"):
1172 expected_timeout |= {
1173 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +00001174 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +00001175 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +00001176 "TestWatchpointConditionAPI.py",
1177 }
Vince Harron0f173ac2015-05-18 19:36:33 +00001178 elif target.startswith("darwin"):
1179 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +00001180 # times out on MBP Retina, Mid 2012
1181 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +00001182 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +00001183 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +00001184 }
Vince Harron06381732015-05-12 23:10:36 +00001185 return expected_timeout
1186
Chaoren Linb6325d02015-08-12 18:02:54 +00001187
Pavel Labathfad30cf2015-06-29 14:16:51 +00001188def getDefaultTimeout(platform_name):
1189 if os.getenv("LLDB_TEST_TIMEOUT"):
1190 return os.getenv("LLDB_TEST_TIMEOUT")
1191
1192 if platform_name is None:
1193 platform_name = sys.platform
1194
1195 if platform_name.startswith("remote-"):
1196 return "10m"
Todd Fiala88722f72015-11-11 05:10:07 +00001197 elif platform_name == 'darwin':
1198 # We are consistently needing more time on a few tests.
1199 return "6m"
Pavel Labathfad30cf2015-06-29 14:16:51 +00001200 else:
1201 return "4m"
1202
Chaoren Linb6325d02015-08-12 18:02:54 +00001203
Vince Harron0b9dbb52015-05-21 18:18:52 +00001204def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +00001205 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +00001206 os.utime(fname, times)
1207
Chaoren Linb6325d02015-08-12 18:02:54 +00001208
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001209def find(pattern, path):
1210 result = []
1211 for root, dirs, files in os.walk(path):
1212 for name in files:
1213 if fnmatch.fnmatch(name, pattern):
1214 result.append(os.path.join(root, name))
1215 return result
1216
Chaoren Linb6325d02015-08-12 18:02:54 +00001217
Todd Fiala8cbeed32015-09-08 22:22:33 +00001218def get_test_runner_strategies(num_threads):
1219 """Returns the test runner strategies by name in a dictionary.
1220
1221 @param num_threads specifies the number of threads/processes
1222 that will be used for concurrent test runners.
1223
1224 @return dictionary with key as test runner strategy name and
1225 value set to a callable object that takes the test work item
1226 and returns a test result tuple.
1227 """
1228 return {
1229 # multiprocessing supports ctrl-c and does not use
1230 # multiprocessing.Pool.
1231 "multiprocessing":
1232 (lambda work_items: multiprocessing_test_runner(
1233 num_threads, work_items)),
1234
1235 # multiprocessing-pool uses multiprocessing.Pool but
1236 # does not support Ctrl-C.
1237 "multiprocessing-pool":
1238 (lambda work_items: multiprocessing_test_runner_pool(
1239 num_threads, work_items)),
1240
1241 # threading uses a hand-rolled worker pool much
1242 # like multiprocessing, but instead uses in-process
1243 # worker threads. This one supports Ctrl-C.
1244 "threading":
1245 (lambda work_items: threading_test_runner(num_threads, work_items)),
1246
1247 # threading-pool uses threading for the workers (in-process)
1248 # and uses the multiprocessing.pool thread-enabled pool.
Todd Fiala68615ce2015-09-15 21:38:04 +00001249 # This does not properly support Ctrl-C.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001250 "threading-pool":
1251 (lambda work_items: threading_test_runner_pool(
1252 num_threads, work_items)),
1253
1254 # serial uses the subprocess-based, single process
1255 # test runner. This provides process isolation but
Todd Fiala68615ce2015-09-15 21:38:04 +00001256 # no concurrent test execution.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001257 "serial":
1258 inprocess_exec_test_runner
1259 }
1260
1261
Todd Fialaea736242015-09-23 15:21:28 +00001262def _remove_option(
1263 args, long_option_name, short_option_name, takes_arg):
Todd Fiala68615ce2015-09-15 21:38:04 +00001264 """Removes option and related option arguments from args array.
Todd Fialaea736242015-09-23 15:21:28 +00001265
1266 This method removes all short/long options that match the given
1267 arguments.
1268
Todd Fiala68615ce2015-09-15 21:38:04 +00001269 @param args the array of command line arguments (in/out)
Todd Fialaea736242015-09-23 15:21:28 +00001270
1271 @param long_option_name the full command line representation of the
1272 long-form option that will be removed (including '--').
1273
1274 @param short_option_name the short version of the command line option
1275 that will be removed (including '-').
1276
1277 @param takes_arg True if the option takes an argument.
1278
Todd Fiala68615ce2015-09-15 21:38:04 +00001279 """
Todd Fialaea736242015-09-23 15:21:28 +00001280 if long_option_name is not None:
1281 regex_string = "^" + long_option_name + "="
1282 long_regex = re.compile(regex_string)
1283 if short_option_name is not None:
1284 # Short options we only match the -X and assume
1285 # any arg is one command line argument jammed together.
1286 # i.e. -O--abc=1 is a single argument in the args list.
1287 # We don't handle -O --abc=1, as argparse doesn't handle
1288 # it, either.
1289 regex_string = "^" + short_option_name
1290 short_regex = re.compile(regex_string)
1291
1292 def remove_long_internal():
1293 """Removes one matching long option from args.
1294 @returns True if one was found and removed; False otherwise.
1295 """
1296 try:
1297 index = args.index(long_option_name)
1298 # Handle the exact match case.
1299 if takes_arg:
1300 removal_count = 2
1301 else:
1302 removal_count = 1
1303 del args[index:index+removal_count]
1304 return True
1305 except ValueError:
1306 # Thanks to argparse not handling options with known arguments
1307 # like other options parsing libraries (see
1308 # https://bugs.python.org/issue9334), we need to support the
1309 # --results-formatter-options={second-level-arguments} (note
1310 # the equal sign to fool the first-level arguments parser into
1311 # not treating the second-level arguments as first-level
1312 # options). We're certainly at risk of getting this wrong
1313 # since now we're forced into the business of trying to figure
1314 # out what is an argument (although I think this
1315 # implementation will suffice).
1316 for index in range(len(args)):
1317 match = long_regex.search(args[index])
1318 if match:
1319 del args[index]
1320 return True
1321 return False
1322
1323 def remove_short_internal():
1324 """Removes one matching short option from args.
1325 @returns True if one was found and removed; False otherwise.
1326 """
Todd Fiala68615ce2015-09-15 21:38:04 +00001327 for index in range(len(args)):
Todd Fialaea736242015-09-23 15:21:28 +00001328 match = short_regex.search(args[index])
Todd Fiala68615ce2015-09-15 21:38:04 +00001329 if match:
Todd Fiala68615ce2015-09-15 21:38:04 +00001330 del args[index]
Todd Fialaea736242015-09-23 15:21:28 +00001331 return True
1332 return False
Todd Fiala68615ce2015-09-15 21:38:04 +00001333
Todd Fialaea736242015-09-23 15:21:28 +00001334 removal_count = 0
1335 while long_option_name is not None and remove_long_internal():
1336 removal_count += 1
1337 while short_option_name is not None and remove_short_internal():
1338 removal_count += 1
1339 if removal_count == 0:
1340 raise Exception(
1341 "failed to find at least one of '{}', '{}' in options".format(
1342 long_option_name, short_option_name))
Todd Fiala68615ce2015-09-15 21:38:04 +00001343
1344
1345def adjust_inferior_options(dotest_argv):
1346 """Adjusts the commandline args array for inferiors.
1347
1348 This method adjusts the inferior dotest commandline options based
1349 on the parallel test runner's options. Some of the inferior options
1350 will need to change to properly handle aggregation functionality.
1351 """
1352 global dotest_options
1353
1354 # If we don't have a session directory, create one.
1355 if not dotest_options.s:
1356 # no session log directory, we need to add this to prevent
1357 # every dotest invocation from creating its own directory
1358 import datetime
1359 # The windows platforms don't like ':' in the pathname.
Zachary Turneraf383ff2015-10-27 22:33:47 +00001360 timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S")
Todd Fiala68615ce2015-09-15 21:38:04 +00001361 dotest_argv.append('-s')
1362 dotest_argv.append(timestamp_started)
1363 dotest_options.s = timestamp_started
1364
1365 # Adjust inferior results formatter options - if the parallel
1366 # test runner is collecting into the user-specified test results,
1367 # we'll have inferiors spawn with the --results-port option and
1368 # strip the original test runner options.
1369 if dotest_options.results_file is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001370 _remove_option(dotest_argv, "--results-file", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001371 if dotest_options.results_port is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001372 _remove_option(dotest_argv, "--results-port", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001373 if dotest_options.results_formatter is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001374 _remove_option(dotest_argv, "--results-formatter", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001375 if dotest_options.results_formatter_options is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001376 _remove_option(dotest_argv, "--results-formatter-option", "-O",
1377 True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001378
Todd Fialacee6a6a2015-11-09 18:51:04 +00001379 # Remove the --curses shortcut if specified.
1380 if dotest_options.curses:
1381 _remove_option(dotest_argv, "--curses", None, False)
1382
Todd Fiala33896a92015-09-18 21:01:13 +00001383 # Remove test runner name if present.
1384 if dotest_options.test_runner_name is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001385 _remove_option(dotest_argv, "--test-runner-name", None, True)
Todd Fiala33896a92015-09-18 21:01:13 +00001386
1387
Todd Fiala83c32e32015-09-22 21:19:40 +00001388def is_darwin_version_lower_than(target_version):
1389 """Checks that os is Darwin and version is lower than target_version.
1390
1391 @param target_version the StrictVersion indicating the version
1392 we're checking against.
1393
1394 @return True if the OS is Darwin (OS X) and the version number of
1395 the OS is less than target_version; False in all other cases.
1396 """
1397 if platform.system() != 'Darwin':
1398 # Can't be Darwin lower than a certain version.
1399 return False
1400
1401 system_version = distutils.version.StrictVersion(platform.mac_ver()[0])
Zachary Turnerbac6e4f2015-11-03 21:37:27 +00001402 return seven.cmp_(system_version, target_version) < 0
Todd Fiala83c32e32015-09-22 21:19:40 +00001403
1404
1405def default_test_runner_name(num_threads):
1406 """Returns the default test runner name for the configuration.
1407
1408 @param num_threads the number of threads/workers this test runner is
1409 supposed to use.
1410
1411 @return the test runner name that should be used by default when
1412 no test runner was explicitly called out on the command line.
1413 """
1414 if num_threads == 1:
1415 # Use the serial runner.
1416 test_runner_name = "serial"
1417 elif os.name == "nt":
Adrian McCarthy040b31d2015-10-12 14:46:57 +00001418 # On Windows, Python uses CRT with a low limit on the number of open
1419 # files. If you have a lot of cores, the threading-pool runner will
Zachary Turnerf0c3f682015-11-06 18:14:31 +00001420 # often fail because it exceeds that limit. It's not clear what the
1421 # right balance is, so until we can investigate it more deeply,
1422 # just use the one that works
1423 test_runner_name = "multiprocessing-pool"
Todd Fiala83c32e32015-09-22 21:19:40 +00001424 elif is_darwin_version_lower_than(
1425 distutils.version.StrictVersion("10.10.0")):
1426 # OS X versions before 10.10 appear to have an issue using
1427 # the threading test runner. Fall back to multiprocessing.
1428 # Supports Ctrl-C.
1429 test_runner_name = "multiprocessing"
1430 else:
1431 # For everyone else, use the ctrl-c-enabled threading support.
1432 # Should use fewer system resources than the multprocessing
1433 # variant.
1434 test_runner_name = "threading"
1435 return test_runner_name
1436
1437
Todd Fiala685a7572015-12-14 21:28:46 +00001438def rerun_tests(test_subdir, tests_for_rerun, dotest_argv):
1439 # Build the list of test files to rerun. Some future time we'll
1440 # enable re-run by test method so we can constrain the rerun set
1441 # to just the method(s) that were in issued within a file.
1442
1443 # Sort rerun files into subdirectories.
1444 print("\nRerunning the following files:")
1445 rerun_files_by_subdir = {}
1446 for test_filename in tests_for_rerun.keys():
1447 # Print the file we'll be rerunning
1448 test_relative_path = os.path.relpath(
1449 test_filename, lldbsuite.lldb_test_root)
1450 print(" {}".format(test_relative_path))
1451
1452 # Store test filenames by subdir.
1453 test_dir = os.path.dirname(test_filename)
1454 test_basename = os.path.basename(test_filename)
1455 if test_dir in rerun_files_by_subdir:
1456 rerun_files_by_subdir[test_dir].append(
1457 (test_basename, test_filename))
1458 else:
1459 rerun_files_by_subdir[test_dir] = [(test_basename, test_filename)]
1460
1461 # Break rerun work up by subdirectory. We do this since
1462 # we have an invariant that states only one test file can
1463 # be run at a time in any given subdirectory (related to
1464 # rules around built inferior test program lifecycle).
1465 rerun_work = []
1466 for files_by_subdir in rerun_files_by_subdir.values():
1467 rerun_work.append((test_subdir, files_by_subdir))
1468
1469 # Run the work with the serial runner.
1470 # Do not update legacy counts, I am getting rid of
1471 # them so no point adding complicated merge logic here.
1472 rerun_thread_count = 1
1473 rerun_runner_name = default_test_runner_name(rerun_thread_count)
1474 runner_strategies_by_name = get_test_runner_strategies(rerun_thread_count)
1475 rerun_runner_func = runner_strategies_by_name[
1476 rerun_runner_name]
1477 if rerun_runner_func is None:
1478 raise Exception(
1479 "failed to find rerun test runner "
1480 "function named '{}'".format(rerun_runner_name))
1481
1482 walk_and_invoke(
1483 rerun_work,
1484 dotest_argv,
1485 rerun_thread_count,
1486 rerun_runner_func)
1487 print("\nTest rerun complete\n")
1488
1489
Zachary Turner80310c22015-12-10 18:51:02 +00001490def main(num_threads, test_subdir, test_runner_name, results_formatter):
Todd Fialafed95662015-09-03 18:58:44 +00001491 """Run dotest.py in inferior mode in parallel.
1492
Todd Fialafed95662015-09-03 18:58:44 +00001493 @param num_threads the parsed value of the num-threads command line
1494 argument.
1495
1496 @param test_subdir optionally specifies a subdir to limit testing
1497 within. May be None if the entire test tree is to be used. This subdir
1498 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001499
1500 @param test_runner_name if specified, contains the test runner
1501 name which selects the strategy used to run the isolated and
1502 optionally concurrent test runner. Specify None to allow the
1503 system to choose the most appropriate test runner given desired
1504 thread count and OS type.
1505
Todd Fiala68615ce2015-09-15 21:38:04 +00001506 @param results_formatter if specified, provides the TestResultsFormatter
1507 instance that will format and output test result data from the
1508 side-channel test results. When specified, inferior dotest calls
1509 will send test results side-channel data over a socket to the parallel
1510 test runner, which will forward them on to results_formatter.
Todd Fialafed95662015-09-03 18:58:44 +00001511 """
1512
Todd Fiala1cc97b42015-09-21 05:42:26 +00001513 # Do not shut down on sighup.
Ying Chend93aa102015-09-23 21:53:18 +00001514 if hasattr(signal, 'SIGHUP'):
1515 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala1cc97b42015-09-21 05:42:26 +00001516
Todd Fialafed95662015-09-03 18:58:44 +00001517 dotest_argv = sys.argv[1:]
1518
Zachary Turner80310c22015-12-10 18:51:02 +00001519 global RESULTS_FORMATTER
Todd Fiala68615ce2015-09-15 21:38:04 +00001520 RESULTS_FORMATTER = results_formatter
Todd Fialafed95662015-09-03 18:58:44 +00001521
Vince Harrond5fa1022015-05-10 15:24:12 +00001522 # We can't use sys.path[0] to determine the script directory
1523 # because it doesn't work under a debugger
Vince Harron8994fed2015-05-22 19:49:23 +00001524 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +00001525 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +00001526 dotest_options = dotest_args.parse_args(parser, dotest_argv)
1527
Todd Fiala68615ce2015-09-15 21:38:04 +00001528 adjust_inferior_options(dotest_argv)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001529
1530 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +00001531
Vince Harrone06a7a82015-05-12 23:12:19 +00001532 # The root directory was specified on the command line
Todd Fiala68615ce2015-09-15 21:38:04 +00001533 test_directory = os.path.dirname(os.path.realpath(__file__))
Todd Fialafed95662015-09-03 18:58:44 +00001534 if test_subdir and len(test_subdir) > 0:
1535 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +00001536 else:
Todd Fialafed95662015-09-03 18:58:44 +00001537 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +00001538
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001539 # clean core files in test tree from previous runs (Linux)
1540 cores = find('core.*', test_subdir)
1541 for core in cores:
1542 os.unlink(core)
1543
Daniel Maleab42556f2013-04-19 18:32:53 +00001544 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +00001545
Todd Fialaa8fee7f2015-12-11 19:44:23 +00001546 # Figure out which test files should be enabled for expected
1547 # timeout
1548 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
1549 if results_formatter is not None:
1550 results_formatter.set_expected_timeouts_by_basename(expected_timeout)
1551
Todd Fiala8cbeed32015-09-08 22:22:33 +00001552 # Figure out which testrunner strategy we'll use.
1553 runner_strategies_by_name = get_test_runner_strategies(num_threads)
1554
1555 # If the user didn't specify a test runner strategy, determine
1556 # the default now based on number of threads and OS type.
1557 if not test_runner_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001558 test_runner_name = default_test_runner_name(num_threads)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001559
1560 if test_runner_name not in runner_strategies_by_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001561 raise Exception(
1562 "specified testrunner name '{}' unknown. Valid choices: {}".format(
1563 test_runner_name,
Zachary Turner606e1e32015-10-23 17:53:51 +00001564 list(runner_strategies_by_name.keys())))
Todd Fiala8cbeed32015-09-08 22:22:33 +00001565 test_runner_func = runner_strategies_by_name[test_runner_name]
1566
Todd Fiala685a7572015-12-14 21:28:46 +00001567 # Collect the files on which we'll run the first test run phase.
Todd Fialad06a9c92015-12-12 00:34:57 +00001568 test_files = []
1569 find_test_files_in_dir_tree(
1570 test_subdir, lambda tdir, tfiles: test_files.append(
1571 (test_subdir, tfiles)))
1572
Todd Fiala685a7572015-12-14 21:28:46 +00001573 # Do the first test run phase.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001574 summary_results = walk_and_invoke(
Todd Fialad06a9c92015-12-12 00:34:57 +00001575 test_files,
1576 dotest_argv,
1577 num_threads,
1578 test_runner_func)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001579
1580 (timed_out, passed, failed, unexpected_successes, pass_count,
1581 fail_count) = summary_results
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +00001582
Todd Fiala685a7572015-12-14 21:28:46 +00001583 # Check if we have any tests to rerun as phase 2.
Todd Fiala93153922015-12-12 19:26:56 +00001584 if results_formatter is not None:
1585 tests_for_rerun = results_formatter.tests_for_rerun
Todd Fiala685a7572015-12-14 21:28:46 +00001586 results_formatter.tests_for_rerun = {}
Todd Fiala93153922015-12-12 19:26:56 +00001587
1588 if tests_for_rerun is not None and len(tests_for_rerun) > 0:
Todd Fiala685a7572015-12-14 21:28:46 +00001589 rerun_file_count = len(tests_for_rerun)
1590 print("\n{} test files marked for rerun\n".format(
1591 rerun_file_count))
1592
1593 # Check if the number of files exceeds the max cutoff. If so,
1594 # we skip the rerun step.
1595 if rerun_file_count > configuration.rerun_max_file_threshold:
1596 print("Skipping rerun: max rerun file threshold ({}) "
1597 "exceeded".format(
1598 configuration.rerun_max_file_threshold))
1599 else:
1600 rerun_tests(test_subdir, tests_for_rerun, dotest_argv)
Todd Fiala93153922015-12-12 19:26:56 +00001601
Todd Fialade9a44e2015-09-22 00:15:50 +00001602 # The results formatter - if present - is done now. Tell it to
1603 # terminate.
1604 if results_formatter is not None:
1605 results_formatter.send_terminate_as_needed()
1606
Vince Harron17f429f2014-12-13 00:08:19 +00001607 timed_out = set(timed_out)
Chaoren Line80372a2015-08-12 18:02:53 +00001608 num_test_files = len(passed) + len(failed)
1609 num_test_cases = pass_count + fail_count
Daniel Maleab42556f2013-04-19 18:32:53 +00001610
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001611 # move core files into session dir
1612 cores = find('core.*', test_subdir)
1613 for core in cores:
1614 dst = core.replace(test_directory, "")[1:]
1615 dst = dst.replace(os.path.sep, "-")
1616 os.rename(core, os.path.join(session_dir, dst))
1617
Vince Harron06381732015-05-12 23:10:36 +00001618 # remove expected timeouts from failures
Vince Harron06381732015-05-12 23:10:36 +00001619 for xtime in expected_timeout:
1620 if xtime in timed_out:
1621 timed_out.remove(xtime)
1622 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001623 result = "ExpectedTimeout"
1624 elif xtime in passed:
1625 result = "UnexpectedCompletion"
1626 else:
1627 result = None # failed
1628
1629 if result:
1630 test_name = os.path.splitext(xtime)[0]
1631 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +00001632
Todd Fiala46a4e342015-12-02 18:48:38 +00001633 # Only run the old summary logic if we don't have a results formatter
1634 # that already prints the summary.
1635 if results_formatter is None or not results_formatter.replaces_summary():
1636 print_legacy_summary = True
1637 else:
1638 print_legacy_summary = False
Zachary Turner4cceca72015-08-14 16:45:32 +00001639
Todd Fiala46a4e342015-12-02 18:48:38 +00001640 if not print_legacy_summary:
Todd Fiala46a4e342015-12-02 18:48:38 +00001641 # Figure out exit code by count of test result types.
1642 issue_count = (
1643 results_formatter.counts_by_test_result_status(
Todd Fiala51831472015-12-09 06:45:43 +00001644 EventBuilder.STATUS_ERROR) +
Todd Fiala46a4e342015-12-02 18:48:38 +00001645 results_formatter.counts_by_test_result_status(
Todd Fiala51831472015-12-09 06:45:43 +00001646 EventBuilder.STATUS_FAILURE) +
1647 results_formatter.counts_by_test_result_status(
Todd Fiala0a7c32b2015-12-09 19:05:44 +00001648 EventBuilder.STATUS_TIMEOUT) +
1649 results_formatter.counts_by_test_result_status(
1650 EventBuilder.STATUS_EXCEPTIONAL_EXIT)
Todd Fiala51831472015-12-09 06:45:43 +00001651 )
1652
Todd Fiala46a4e342015-12-02 18:48:38 +00001653 # Return with appropriate result code
1654 if issue_count > 0:
1655 sys.exit(1)
1656 else:
1657 sys.exit(0)
1658 else:
1659 # Print the legacy test results summary.
1660 print()
1661 sys.stdout.write("Ran %d test suites" % num_test_files)
1662 if num_test_files > 0:
1663 sys.stdout.write(" (%d failed) (%f%%)" % (
1664 len(failed), 100.0 * len(failed) / num_test_files))
1665 print()
1666 sys.stdout.write("Ran %d test cases" % num_test_cases)
1667 if num_test_cases > 0:
1668 sys.stdout.write(" (%d failed) (%f%%)" % (
1669 fail_count, 100.0 * fail_count / num_test_cases))
1670 print()
1671 exit_code = 0
1672
1673 if len(failed) > 0:
1674 failed.sort()
1675 print("Failing Tests (%d)" % len(failed))
1676 for f in failed:
1677 print("%s: LLDB (suite) :: %s (%s)" % (
1678 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
1679 ))
1680 exit_code = 1
1681
1682 if len(unexpected_successes) > 0:
1683 unexpected_successes.sort()
1684 print("\nUnexpected Successes (%d)" % len(unexpected_successes))
1685 for u in unexpected_successes:
1686 print("UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info))
Zachary Turner4cceca72015-08-14 16:45:32 +00001687
1688 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001689
1690if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +00001691 sys.stderr.write(
1692 "error: dosep.py no longer supports being called directly. "
1693 "Please call dotest.py directly. The dosep.py-specific arguments "
1694 "have been added under the Parallel processing arguments.\n")
1695 sys.exit(128)