blob: 7af953fb698dc46d5384092fc91b6ea53db3676a [file] [log] [blame]
Johnny Chene8d9dc62011-10-31 19:04:07 +00001#!/usr/bin/env python
2
3"""
4Run the test suite using a separate process for each test file.
Vince Harronede59652015-01-08 02:11:26 +00005
Siva Chandra2d7832e2015-05-08 23:08:53 +00006Each test will run with a time limit of 10 minutes by default.
Vince Harronede59652015-01-08 02:11:26 +00007
Siva Chandra2d7832e2015-05-08 23:08:53 +00008Override the default time limit of 10 minutes by setting
Vince Harronede59652015-01-08 02:11:26 +00009the environment variable LLDB_TEST_TIMEOUT.
10
11E.g., export LLDB_TEST_TIMEOUT=10m
12
13Override the time limit for individual tests by setting
14the environment variable LLDB_[TEST NAME]_TIMEOUT.
15
16E.g., export LLDB_TESTCONCURRENTEVENTS_TIMEOUT=2m
17
18Set to "0" to run without time limit.
19
20E.g., export LLDB_TEST_TIMEOUT=0
21or export LLDB_TESTCONCURRENTEVENTS_TIMEOUT=0
Vince Harrondcc2b9f2015-05-27 04:40:36 +000022
Chaoren Linb6325d02015-08-12 18:02:54 +000023To collect core files for timed out tests,
24do the following before running dosep.py
Vince Harrondcc2b9f2015-05-27 04:40:36 +000025
26OSX
27ulimit -c unlimited
28sudo sysctl -w kern.corefile=core.%P
29
30Linux:
31ulimit -c unlimited
32echo core.%p | sudo tee /proc/sys/kernel/core_pattern
Johnny Chene8d9dc62011-10-31 19:04:07 +000033"""
34
Todd Fiala2d3754d2015-09-29 22:19:06 +000035# system packages and modules
Todd Fiala68615ce2015-09-15 21:38:04 +000036import asyncore
Todd Fiala83c32e32015-09-22 21:19:40 +000037import distutils.version
Vince Harrondcc2b9f2015-05-27 04:40:36 +000038import fnmatch
Todd Fiala8cbeed32015-09-08 22:22:33 +000039import multiprocessing
40import multiprocessing.pool
41import os
Todd Fiala3f0a3602014-07-08 06:42:37 +000042import platform
Todd Fiala8cbeed32015-09-08 22:22:33 +000043import Queue
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
49# Add our local test_runner/lib dir to the python path.
50sys.path.append(os.path.join(os.path.dirname(__file__), "test_runner", "lib"))
51
52# Our packages and modules
Todd Fiala68615ce2015-09-15 21:38:04 +000053import dotest_channels
Todd Fiala8cbeed32015-09-08 22:22:33 +000054import dotest_args
Todd Fiala2d3754d2015-09-29 22:19:06 +000055import lldb_utils
56import process_control
Vince Harron17f429f2014-12-13 00:08:19 +000057
Vince Harron17f429f2014-12-13 00:08:19 +000058# Status codes for running command with timeout.
59eTimedOut, ePassed, eFailed = 124, 0, 1
60
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000061output_lock = None
62test_counter = None
63total_tests = None
Chaoren Linffc63b02015-08-12 18:02:49 +000064test_name_len = None
Pavel Labath05ab2372015-07-06 15:57:52 +000065dotest_options = None
Zachary Turner38e64172015-08-10 17:46:11 +000066output_on_success = False
Todd Fiala68615ce2015-09-15 21:38:04 +000067RESULTS_FORMATTER = None
68RUNNER_PROCESS_ASYNC_MAP = None
69RESULTS_LISTENER_CHANNEL = None
Chaoren Linb6325d02015-08-12 18:02:54 +000070
Todd Fiala33896a92015-09-18 21:01:13 +000071"""Contains an optional function pointer that can return the worker index
72 for the given thread/process calling it. Returns a 0-based index."""
73GET_WORKER_INDEX = None
74
Todd Fiala1cc97b42015-09-21 05:42:26 +000075
Todd Fiala33896a92015-09-18 21:01:13 +000076def setup_global_variables(
77 lock, counter, total, name_len, options, worker_index_map):
Chaoren Linffc63b02015-08-12 18:02:49 +000078 global output_lock, test_counter, total_tests, test_name_len
79 global dotest_options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000080 output_lock = lock
81 test_counter = counter
82 total_tests = total
Chaoren Linffc63b02015-08-12 18:02:49 +000083 test_name_len = name_len
Pavel Labath05ab2372015-07-06 15:57:52 +000084 dotest_options = options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000085
Todd Fiala33896a92015-09-18 21:01:13 +000086 if worker_index_map is not None:
87 # We'll use the output lock for this to avoid sharing another lock.
88 # This won't be used much.
89 index_lock = lock
90
91 def get_worker_index_use_pid():
92 """Returns a 0-based, process-unique index for the worker."""
93 pid = os.getpid()
94 with index_lock:
95 if pid not in worker_index_map:
96 worker_index_map[pid] = len(worker_index_map)
97 return worker_index_map[pid]
98
99 global GET_WORKER_INDEX
100 GET_WORKER_INDEX = get_worker_index_use_pid
101
Chaoren Linb6325d02015-08-12 18:02:54 +0000102
Zachary Turner38e64172015-08-10 17:46:11 +0000103def report_test_failure(name, command, output):
104 global output_lock
105 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000106 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
107 print >> sys.stderr
108 print >> sys.stderr, output
109 print >> sys.stderr, "[%s FAILED]" % name
110 print >> sys.stderr, "Command invoked: %s" % ' '.join(command)
Chaoren Linffc63b02015-08-12 18:02:49 +0000111 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000112
Chaoren Linb6325d02015-08-12 18:02:54 +0000113
Zachary Turner38e64172015-08-10 17:46:11 +0000114def report_test_pass(name, output):
115 global output_lock, output_on_success
116 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000117 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
118 if output_on_success:
119 print >> sys.stderr
120 print >> sys.stderr, output
121 print >> sys.stderr, "[%s PASSED]" % name
Chaoren Linffc63b02015-08-12 18:02:49 +0000122 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000123
Chaoren Linb6325d02015-08-12 18:02:54 +0000124
Chaoren Linffc63b02015-08-12 18:02:49 +0000125def update_progress(test_name=""):
126 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000127 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000128 counter_len = len(str(total_tests))
Greg Clayton1827fc22015-09-19 00:39:09 +0000129 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
130 sys.stderr.write(
131 "\r%*d out of %d test suites processed - %-*s" %
132 (counter_len, test_counter.value, total_tests,
133 test_name_len.value, test_name))
Chaoren Linffc63b02015-08-12 18:02:49 +0000134 if len(test_name) > test_name_len.value:
135 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000136 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000137 sys.stdout.flush()
138 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000139
Chaoren Linb6325d02015-08-12 18:02:54 +0000140
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000141def parse_test_results(output):
142 passes = 0
143 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000144 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000145 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000146 pass_count = re.search("^RESULT:.*([0-9]+) passes",
147 result, re.MULTILINE)
148 fail_count = re.search("^RESULT:.*([0-9]+) failures",
149 result, re.MULTILINE)
150 error_count = re.search("^RESULT:.*([0-9]+) errors",
151 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000152 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
153 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000154 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000155 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000156 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000157 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000158 if unexpected_success_count is not None:
159 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000160 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000161 failures = failures + int(error_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000162 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000163
Chaoren Linb6325d02015-08-12 18:02:54 +0000164
Todd Fiala2d3754d2015-09-29 22:19:06 +0000165class DoTestProcessDriver(process_control.ProcessDriver):
166 """Drives the dotest.py inferior process and handles bookkeeping."""
167 def __init__(self, output_file, output_file_lock, pid_events, file_name,
168 soft_terminate_timeout):
169 super(DoTestProcessDriver, self).__init__(
170 soft_terminate_timeout=soft_terminate_timeout)
171 self.output_file = output_file
172 self.output_lock = lldb_utils.OptionalWith(output_file_lock)
173 self.pid_events = pid_events
174 self.results = None
175 self.file_name = file_name
176
177 def write(self, content):
178 with self.output_lock:
179 self.output_file.write(content)
180
181 def on_process_started(self):
182 if self.pid_events:
183 self.pid_events.put_nowait(('created', self.process.pid))
184
185 def on_process_exited(self, command, output, was_timeout, exit_status):
186 if self.pid_events:
187 # No point in culling out those with no exit_status (i.e.
188 # those we failed to kill). That would just cause
189 # downstream code to try to kill it later on a Ctrl-C. At
190 # this point, a best-effort-to-kill already took place. So
191 # call it destroyed here.
192 self.pid_events.put_nowait(('destroyed', self.process.pid))
193
194 # Override the exit status if it was a timeout.
195 if was_timeout:
196 exit_status = eTimedOut
197
198 # If we didn't end up with any output, call it empty for
199 # stdout/stderr.
200 if output is None:
201 output = ('', '')
202
203 # Now parse the output.
204 passes, failures, unexpected_successes = parse_test_results(output)
205 if exit_status == 0:
206 # stdout does not have any useful information from 'dotest.py',
207 # only stderr does.
208 report_test_pass(self.file_name, output[1])
209 else:
210 report_test_failure(self.file_name, command, output[1])
211
212 # Save off the results for the caller.
213 self.results = (
214 self.file_name,
215 exit_status,
216 passes,
217 failures,
218 unexpected_successes)
219
220
221def get_soft_terminate_timeout():
222 # Defaults to 10 seconds, but can set
223 # LLDB_TEST_SOFT_TERMINATE_TIMEOUT to a floating point
224 # number in seconds. This value indicates how long
225 # the test runner will wait for the dotest inferior to
226 # handle a timeout via a soft terminate before it will
227 # assume that failed and do a hard terminate.
228
229 # TODO plumb through command-line option
230 return float(os.environ.get('LLDB_TEST_SOFT_TERMINATE_TIMEOUT', 10.0))
231
232
233def want_core_on_soft_terminate():
234 # TODO plumb through command-line option
235 if platform.system() == 'Linux':
236 return True
237 else:
238 return False
Todd Fialada817b62015-09-22 18:05:11 +0000239
240
Todd Fiala8cbeed32015-09-08 22:22:33 +0000241def call_with_timeout(command, timeout, name, inferior_pid_events):
Todd Fiala2d3754d2015-09-29 22:19:06 +0000242 # Add our worker index (if we have one) to all test events
243 # from this inferior.
Todd Fiala33896a92015-09-18 21:01:13 +0000244 if GET_WORKER_INDEX is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000245 try:
246 worker_index = GET_WORKER_INDEX()
247 command.extend([
Todd Fiala2d3754d2015-09-29 22:19:06 +0000248 "--event-add-entries",
249 "worker_index={}:int".format(worker_index)])
250 except: # pylint: disable=bare-except
251 # Ctrl-C does bad things to multiprocessing.Manager.dict()
252 # lookup. Just swallow it.
Todd Fialae83f1402015-09-18 22:45:31 +0000253 pass
254
Todd Fiala2d3754d2015-09-29 22:19:06 +0000255 # Create the inferior dotest.py ProcessDriver.
256 soft_terminate_timeout = get_soft_terminate_timeout()
257 want_core = want_core_on_soft_terminate()
Todd Fiala68615ce2015-09-15 21:38:04 +0000258
Todd Fiala2d3754d2015-09-29 22:19:06 +0000259 process_driver = DoTestProcessDriver(
260 sys.stdout,
261 output_lock,
262 inferior_pid_events,
263 name,
264 soft_terminate_timeout)
Todd Fiala68615ce2015-09-15 21:38:04 +0000265
Todd Fiala2d3754d2015-09-29 22:19:06 +0000266 # Run it with a timeout.
267 process_driver.run_command_with_timeout(command, timeout, want_core)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000268
Todd Fiala2d3754d2015-09-29 22:19:06 +0000269 # Return the results.
270 if not process_driver.results:
271 # This is truly exceptional. Even a failing or timed out
272 # binary should have called the results-generation code.
273 raise Exception("no test results were generated whatsoever")
274 return process_driver.results
Johnny Chene8d9dc62011-10-31 19:04:07 +0000275
Chaoren Linb6325d02015-08-12 18:02:54 +0000276
Todd Fiala8cbeed32015-09-08 22:22:33 +0000277def process_dir(root, files, test_root, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000278 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000279 results = []
Steve Puccibefe2b12014-03-07 00:01:11 +0000280 for name in files:
Zachary Turnerf6896b02015-01-05 19:37:03 +0000281 script_file = os.path.join(test_root, "dotest.py")
Zachary Turnerf6896b02015-01-05 19:37:03 +0000282 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000283 dotest_argv +
Todd Fialafed95662015-09-03 18:58:44 +0000284 ["--inferior", "-p", name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000285
286 timeout_name = os.path.basename(os.path.splitext(name)[0]).upper()
287
Chaoren Linb6325d02015-08-12 18:02:54 +0000288 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
289 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000290
Todd Fiala8cbeed32015-09-08 22:22:33 +0000291 results.append(call_with_timeout(
292 command, timeout, name, inferior_pid_events))
Vince Harron17f429f2014-12-13 00:08:19 +0000293
Zachary Turner4cceca72015-08-14 16:45:32 +0000294 # result = (name, status, passes, failures, unexpected_successes)
295 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000296 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000297 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000298 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000299 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000300 if status != ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000301 unexpected_passes = [name for name, _, _, _, unexpected_successes in results
302 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000303
Chaoren Line80372a2015-08-12 18:02:53 +0000304 pass_count = sum([result[2] for result in results])
305 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000306
Zachary Turner4cceca72015-08-14 16:45:32 +0000307 return (timed_out, passed, failed, unexpected_passes, pass_count, fail_count)
Steve Puccibefe2b12014-03-07 00:01:11 +0000308
309in_q = None
310out_q = None
311
Chaoren Linb6325d02015-08-12 18:02:54 +0000312
Todd Fiala8cbeed32015-09-08 22:22:33 +0000313def process_dir_worker_multiprocessing(
314 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000315 a_dotest_options, job_queue, result_queue, inferior_pid_events,
316 worker_index_map):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000317 """Worker thread main loop when in multiprocessing mode.
Steve Puccibefe2b12014-03-07 00:01:11 +0000318 Takes one directory specification at a time and works on it."""
Todd Fiala8cbeed32015-09-08 22:22:33 +0000319
320 # Shut off interrupt handling in the child process.
321 signal.signal(signal.SIGINT, signal.SIG_IGN)
Ying Chend93aa102015-09-23 21:53:18 +0000322 if hasattr(signal, 'SIGHUP'):
323 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000324
325 # Setup the global state for the worker process.
326 setup_global_variables(
327 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000328 a_dotest_options, worker_index_map)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000329
330 # Keep grabbing entries from the queue until done.
331 while not job_queue.empty():
332 try:
333 job = job_queue.get(block=False)
334 result = process_dir(job[0], job[1], job[2], job[3],
335 inferior_pid_events)
336 result_queue.put(result)
337 except Queue.Empty:
338 # Fine, we're done.
339 pass
Steve Puccibefe2b12014-03-07 00:01:11 +0000340
Chaoren Linb6325d02015-08-12 18:02:54 +0000341
Todd Fiala8cbeed32015-09-08 22:22:33 +0000342def process_dir_worker_multiprocessing_pool(args):
343 return process_dir(*args)
344
345
Todd Fiala68615ce2015-09-15 21:38:04 +0000346def process_dir_worker_threading(job_queue, result_queue, inferior_pid_events):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000347 """Worker thread main loop when in threading mode.
348
349 This one supports the hand-rolled pooling support.
350
351 Takes one directory specification at a time and works on it."""
352
353 # Keep grabbing entries from the queue until done.
354 while not job_queue.empty():
355 try:
356 job = job_queue.get(block=False)
357 result = process_dir(job[0], job[1], job[2], job[3],
358 inferior_pid_events)
359 result_queue.put(result)
360 except Queue.Empty:
361 # Fine, we're done.
362 pass
363
364
365def process_dir_worker_threading_pool(args):
366 return process_dir(*args)
367
368
369def process_dir_mapper_inprocess(args):
370 """Map adapter for running the subprocess-based, non-threaded test runner.
371
372 @param args the process work item tuple
373 @return the test result tuple
374 """
375 return process_dir(*args)
376
377
378def collect_active_pids_from_pid_events(event_queue):
379 """
380 Returns the set of what should be active inferior pids based on
381 the event stream.
382
383 @param event_queue a multiprocessing.Queue containing events of the
384 form:
385 ('created', pid)
386 ('destroyed', pid)
387
388 @return set of inferior dotest.py pids activated but never completed.
389 """
390 active_pid_set = set()
391 while not event_queue.empty():
392 pid_event = event_queue.get_nowait()
393 if pid_event[0] == 'created':
394 active_pid_set.add(pid_event[1])
395 elif pid_event[0] == 'destroyed':
396 active_pid_set.remove(pid_event[1])
397 return active_pid_set
398
399
400def kill_all_worker_processes(workers, inferior_pid_events):
401 """
402 Kills all specified worker processes and their process tree.
403
404 @param workers a list of multiprocess.Process worker objects.
405 @param inferior_pid_events a multiprocess.Queue that contains
406 all inferior create and destroy events. Used to construct
407 the list of child pids still outstanding that need to be killed.
408 """
409 for worker in workers:
410 worker.terminate()
411 worker.join()
412
413 # Add all the child test pids created.
414 active_pid_set = collect_active_pids_from_pid_events(
415 inferior_pid_events)
416 for inferior_pid in active_pid_set:
417 print "killing inferior pid {}".format(inferior_pid)
418 os.kill(inferior_pid, signal.SIGKILL)
419
420
421def kill_all_worker_threads(workers, inferior_pid_events):
422 """
423 Kills all specified worker threads and their process tree.
424
425 @param workers a list of multiprocess.Process worker objects.
426 @param inferior_pid_events a multiprocess.Queue that contains
427 all inferior create and destroy events. Used to construct
428 the list of child pids still outstanding that need to be killed.
429 """
430
431 # Add all the child test pids created.
432 active_pid_set = collect_active_pids_from_pid_events(
433 inferior_pid_events)
434 for inferior_pid in active_pid_set:
435 print "killing inferior pid {}".format(inferior_pid)
436 os.kill(inferior_pid, signal.SIGKILL)
437
438 # We don't have a way to nuke the threads. However, since we killed
439 # all the inferiors, and we drained the job queue, this will be
440 # good enough. Wait cleanly for each worker thread to wrap up.
441 for worker in workers:
442 worker.join()
443
444
445def find_test_files_in_dir_tree(dir_root, found_func):
446 """Calls found_func for all the test files in the given dir hierarchy.
447
448 @param dir_root the path to the directory to start scanning
449 for test files. All files in this directory and all its children
450 directory trees will be searched.
451
452 @param found_func a callable object that will be passed
453 the parent directory (relative to dir_root) and the list of
454 test files from within that directory.
455 """
456 for root, _, files in os.walk(dir_root, topdown=False):
457 def is_test_filename(test_dir, base_filename):
458 """Returns True if the given filename matches the test name format.
459
460 @param test_dir the directory to check. Should be absolute or
461 relative to current working directory.
462
463 @param base_filename the base name of the filename to check for a
464 dherence to the python test case filename format.
465
466 @return True if name matches the python test case filename format.
467 """
468 # Not interested in symbolically linked files.
469 if os.path.islink(os.path.join(test_dir, base_filename)):
470 return False
471 # Only interested in test files with the "Test*.py" naming pattern.
472 return (base_filename.startswith("Test") and
473 base_filename.endswith(".py"))
474
475 tests = [filename for filename in files
476 if is_test_filename(root, filename)]
477 if tests:
478 found_func(root, tests)
479
480
481def initialize_global_vars_common(num_threads, test_work_items):
482 global total_tests, test_counter, test_name_len
Greg Clayton1827fc22015-09-19 00:39:09 +0000483
Todd Fiala8cbeed32015-09-08 22:22:33 +0000484 total_tests = sum([len(item[1]) for item in test_work_items])
485 test_counter = multiprocessing.Value('i', 0)
486 test_name_len = multiprocessing.Value('i', 0)
Greg Clayton1827fc22015-09-19 00:39:09 +0000487 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
488 print >> sys.stderr, "Testing: %d test suites, %d thread%s" % (
489 total_tests, num_threads, (num_threads > 1) * "s")
Todd Fiala8cbeed32015-09-08 22:22:33 +0000490 update_progress()
491
492
493def initialize_global_vars_multiprocessing(num_threads, test_work_items):
494 # Initialize the global state we'll use to communicate with the
495 # rest of the flat module.
496 global output_lock
497 output_lock = multiprocessing.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000498
Todd Fiala8cbeed32015-09-08 22:22:33 +0000499 initialize_global_vars_common(num_threads, test_work_items)
500
501
502def initialize_global_vars_threading(num_threads, test_work_items):
Todd Fiala33896a92015-09-18 21:01:13 +0000503 """Initializes global variables used in threading mode.
504 @param num_threads specifies the number of workers used.
505 @param test_work_items specifies all the work items
506 that will be processed.
507 """
Todd Fiala8cbeed32015-09-08 22:22:33 +0000508 # Initialize the global state we'll use to communicate with the
509 # rest of the flat module.
510 global output_lock
511 output_lock = threading.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000512
513 index_lock = threading.RLock()
514 index_map = {}
515
516 def get_worker_index_threading():
517 """Returns a 0-based, thread-unique index for the worker thread."""
518 thread_id = threading.current_thread().ident
519 with index_lock:
520 if thread_id not in index_map:
521 index_map[thread_id] = len(index_map)
522 return index_map[thread_id]
523
524
525 global GET_WORKER_INDEX
526 GET_WORKER_INDEX = get_worker_index_threading
527
Todd Fiala8cbeed32015-09-08 22:22:33 +0000528 initialize_global_vars_common(num_threads, test_work_items)
529
530
Todd Fiala68615ce2015-09-15 21:38:04 +0000531def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler):
532 """Provides a main loop that is Ctrl-C protected.
533
534 The main loop calls the main_op_func() repeatedly until done_func()
535 returns true. The ctrl_c_handler() method is called with a single
536 int parameter that contains the number of times the ctrl_c has been
537 hit (starting with 1). The ctrl_c_handler() should mutate whatever
538 it needs to have the done_func() return True as soon as it is desired
539 to exit the loop.
540 """
541 done = False
542 ctrl_c_count = 0
543
544 while not done:
545 try:
546 # See if we're done. Start with done check since it is
547 # the first thing executed after a Ctrl-C handler in the
548 # following loop.
549 done = done_func()
550 if not done:
551 # Run the main op once.
552 main_op_func()
553
554 except KeyboardInterrupt:
555 ctrl_c_count += 1
556 ctrl_c_handler(ctrl_c_count)
557
558
559def pump_workers_and_asyncore_map(workers, asyncore_map):
560 """Prunes out completed workers and maintains the asyncore loop.
561
562 The asyncore loop contains the optional socket listener
563 and handlers. When all workers are complete, this method
564 takes care of stopping the listener. It also runs the
565 asyncore loop for the given async map for 10 iterations.
566
567 @param workers the list of worker Thread/Process instances.
568
569 @param asyncore_map the asyncore threading-aware map that
570 indicates which channels are in use and still alive.
571 """
572
573 # Check on all the workers, removing them from the workers
574 # list as they complete.
575 dead_workers = []
576 for worker in workers:
577 # This non-blocking join call is what allows us
578 # to still receive keyboard interrupts.
579 worker.join(0.01)
580 if not worker.is_alive():
581 dead_workers.append(worker)
582 # Clear out the completed workers
583 for dead_worker in dead_workers:
584 workers.remove(dead_worker)
585
586 # If there are no more workers and there is a listener,
587 # close the listener.
588 global RESULTS_LISTENER_CHANNEL
589 if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None:
590 RESULTS_LISTENER_CHANNEL.close()
591 RESULTS_LISTENER_CHANNEL = None
592
593 # Pump the asyncore map if it isn't empty.
594 if len(asyncore_map) > 0:
595 asyncore.loop(0.1, False, asyncore_map, 10)
596
597
598def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events,
599 stop_all_inferiors_func):
600 """Performs the appropriate ctrl-c action for non-pool parallel test runners
601
602 @param ctrl_c_count starting with 1, indicates the number of times ctrl-c
603 has been intercepted. The value is 1 on the first intercept, 2 on the
604 second, etc.
605
606 @param job_queue a Queue object that contains the work still outstanding
607 (i.e. hasn't been assigned to a worker yet).
608
609 @param workers list of Thread or Process workers.
610
611 @param inferior_pid_events specifies a Queue of inferior process
612 construction and destruction events. Used to build the list of inferior
613 processes that should be killed if we get that far.
614
615 @param stop_all_inferiors_func a callable object that takes the
616 workers and inferior_pid_events parameters (in that order) if a hard
617 stop is to be used on the workers.
618 """
619
620 # Print out which Ctrl-C we're handling.
621 key_name = [
622 "first",
623 "second",
624 "third",
625 "many"]
626
627 if ctrl_c_count < len(key_name):
628 name_index = ctrl_c_count - 1
629 else:
630 name_index = len(key_name) - 1
631 message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index])
632 with output_lock:
633 print message
634
635 if ctrl_c_count == 1:
636 # Remove all outstanding items from the work queue so we stop
637 # doing any more new work.
638 while not job_queue.empty():
639 try:
640 # Just drain it to stop more work from being started.
641 job_queue.get_nowait()
642 except Queue.Empty:
643 pass
644 with output_lock:
645 print "Stopped more work from being started."
646 elif ctrl_c_count == 2:
647 # Try to stop all inferiors, even the ones currently doing work.
648 stop_all_inferiors_func(workers, inferior_pid_events)
649 else:
650 with output_lock:
651 print "All teardown activities kicked off, should finish soon."
652
653
654def workers_and_async_done(workers, async_map):
655 """Returns True if the workers list and asyncore channels are all done.
656
657 @param workers list of workers (threads/processes). These must adhere
658 to the threading Thread or multiprocessing.Process interface.
659
660 @param async_map the threading-aware asyncore channel map to check
661 for live channels.
662
663 @return False if the workers list exists and has any entries in it, or
664 if the async_map exists and has any entries left in it; otherwise, True.
665 """
666 if workers is not None and len(workers) > 0:
667 # We're not done if we still have workers left.
668 return False
669 if async_map is not None and len(async_map) > 0:
670 return False
671 # We're done.
672 return True
673
674
Todd Fiala8cbeed32015-09-08 22:22:33 +0000675def multiprocessing_test_runner(num_threads, test_work_items):
676 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
677
678 This concurrent test runner is based on the multiprocessing
679 library, and rolls its own worker pooling strategy so it
680 can handle Ctrl-C properly.
681
682 This test runner is known to have an issue running on
683 Windows platforms.
684
685 @param num_threads the number of worker processes to use.
686
687 @param test_work_items the iterable of test work item tuples
688 to run.
689 """
690
691 # Initialize our global state.
692 initialize_global_vars_multiprocessing(num_threads, test_work_items)
693
694 # Create jobs.
695 job_queue = multiprocessing.Queue(len(test_work_items))
696 for test_work_item in test_work_items:
697 job_queue.put(test_work_item)
698
699 result_queue = multiprocessing.Queue(len(test_work_items))
700
701 # Create queues for started child pids. Terminating
702 # the multiprocess processes does not terminate the
703 # child processes they spawn. We can remove this tracking
704 # if/when we move to having the multiprocess process directly
705 # perform the test logic. The Queue size needs to be able to
706 # hold 2 * (num inferior dotest.py processes started) entries.
707 inferior_pid_events = multiprocessing.Queue(4096)
708
Todd Fiala33896a92015-09-18 21:01:13 +0000709 # Worker dictionary allows each worker to figure out its worker index.
710 manager = multiprocessing.Manager()
711 worker_index_map = manager.dict()
712
Todd Fiala8cbeed32015-09-08 22:22:33 +0000713 # Create workers. We don't use multiprocessing.Pool due to
714 # challenges with handling ^C keyboard interrupts.
715 workers = []
716 for _ in range(num_threads):
717 worker = multiprocessing.Process(
718 target=process_dir_worker_multiprocessing,
719 args=(output_lock,
720 test_counter,
721 total_tests,
722 test_name_len,
723 dotest_options,
724 job_queue,
725 result_queue,
Todd Fiala33896a92015-09-18 21:01:13 +0000726 inferior_pid_events,
727 worker_index_map))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000728 worker.start()
729 workers.append(worker)
730
Todd Fiala68615ce2015-09-15 21:38:04 +0000731 # Main loop: wait for all workers to finish and wait for
732 # the socket handlers to wrap up.
733 ctrl_c_loop(
734 # Main operation of loop
735 lambda: pump_workers_and_asyncore_map(
736 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000737
Todd Fiala68615ce2015-09-15 21:38:04 +0000738 # Return True when we're done with the main loop.
739 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000740
Todd Fiala68615ce2015-09-15 21:38:04 +0000741 # Indicate what we do when we receive one or more Ctrl-Cs.
742 lambda ctrl_c_count: handle_ctrl_c(
743 ctrl_c_count, job_queue, workers, inferior_pid_events,
744 kill_all_worker_processes))
745
746 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000747 test_results = []
748 while not result_queue.empty():
749 test_results.append(result_queue.get(block=False))
750 return test_results
751
752
Todd Fiala68615ce2015-09-15 21:38:04 +0000753def map_async_run_loop(future, channel_map, listener_channel):
754 """Blocks until the Pool.map_async completes and the channel completes.
755
756 @param future an AsyncResult instance from a Pool.map_async() call.
757
758 @param channel_map the asyncore dispatch channel map that should be pumped.
759 Optional: may be None.
760
761 @param listener_channel the channel representing a listener that should be
762 closed once the map_async results are available.
763
764 @return the results from the async_result instance.
765 """
766 map_results = None
767
768 done = False
769 while not done:
770 # Check if we need to reap the map results.
771 if map_results is None:
772 if future.ready():
773 # Get the results.
774 map_results = future.get()
775
776 # Close the runner process listener channel if we have
777 # one: no more connections will be incoming.
778 if listener_channel is not None:
779 listener_channel.close()
780
781 # Pump the asyncore loop if we have a listener socket.
782 if channel_map is not None:
783 asyncore.loop(0.01, False, channel_map, 10)
784
785 # Figure out if we're done running.
786 done = map_results is not None
787 if channel_map is not None:
788 # We have a runner process async map. Check if it
789 # is complete.
790 if len(channel_map) > 0:
791 # We still have an asyncore channel running. Not done yet.
792 done = False
793
794 return map_results
795
796
Todd Fiala8cbeed32015-09-08 22:22:33 +0000797def multiprocessing_test_runner_pool(num_threads, test_work_items):
798 # Initialize our global state.
799 initialize_global_vars_multiprocessing(num_threads, test_work_items)
800
Todd Fiala33896a92015-09-18 21:01:13 +0000801 manager = multiprocessing.Manager()
802 worker_index_map = manager.dict()
803
Todd Fiala8cbeed32015-09-08 22:22:33 +0000804 pool = multiprocessing.Pool(
805 num_threads,
806 initializer=setup_global_variables,
807 initargs=(output_lock, test_counter, total_tests, test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000808 dotest_options, worker_index_map))
Todd Fiala68615ce2015-09-15 21:38:04 +0000809
810 # Start the map operation (async mode).
811 map_future = pool.map_async(
812 process_dir_worker_multiprocessing_pool, test_work_items)
813 return map_async_run_loop(
814 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000815
816
817def threading_test_runner(num_threads, test_work_items):
818 """Provides hand-wrapped pooling threading-based test runner adapter
819 with Ctrl-C support.
820
821 This concurrent test runner is based on the threading
822 library, and rolls its own worker pooling strategy so it
823 can handle Ctrl-C properly.
824
825 @param num_threads the number of worker processes to use.
826
827 @param test_work_items the iterable of test work item tuples
828 to run.
829 """
830
831 # Initialize our global state.
832 initialize_global_vars_threading(num_threads, test_work_items)
833
834 # Create jobs.
835 job_queue = Queue.Queue()
836 for test_work_item in test_work_items:
837 job_queue.put(test_work_item)
838
839 result_queue = Queue.Queue()
840
841 # Create queues for started child pids. Terminating
842 # the threading threads does not terminate the
843 # child processes they spawn.
844 inferior_pid_events = Queue.Queue()
845
846 # Create workers. We don't use multiprocessing.pool.ThreadedPool
847 # due to challenges with handling ^C keyboard interrupts.
848 workers = []
849 for _ in range(num_threads):
850 worker = threading.Thread(
851 target=process_dir_worker_threading,
Todd Fiala68615ce2015-09-15 21:38:04 +0000852 args=(job_queue,
Todd Fiala8cbeed32015-09-08 22:22:33 +0000853 result_queue,
854 inferior_pid_events))
855 worker.start()
856 workers.append(worker)
857
Todd Fiala68615ce2015-09-15 21:38:04 +0000858 # Main loop: wait for all workers to finish and wait for
859 # the socket handlers to wrap up.
860 ctrl_c_loop(
861 # Main operation of loop
862 lambda: pump_workers_and_asyncore_map(
863 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000864
Todd Fiala68615ce2015-09-15 21:38:04 +0000865 # Return True when we're done with the main loop.
866 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000867
Todd Fiala68615ce2015-09-15 21:38:04 +0000868 # Indicate what we do when we receive one or more Ctrl-Cs.
869 lambda ctrl_c_count: handle_ctrl_c(
870 ctrl_c_count, job_queue, workers, inferior_pid_events,
871 kill_all_worker_threads))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000872
Todd Fiala68615ce2015-09-15 21:38:04 +0000873 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000874 test_results = []
875 while not result_queue.empty():
876 test_results.append(result_queue.get(block=False))
877 return test_results
878
879
880def threading_test_runner_pool(num_threads, test_work_items):
881 # Initialize our global state.
882 initialize_global_vars_threading(num_threads, test_work_items)
883
Todd Fiala68615ce2015-09-15 21:38:04 +0000884 pool = multiprocessing.pool.ThreadPool(num_threads)
885 map_future = pool.map_async(
886 process_dir_worker_threading_pool, test_work_items)
887
888 return map_async_run_loop(
889 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
890
891
892def asyncore_run_loop(channel_map):
893 try:
894 asyncore.loop(None, False, channel_map)
895 except:
896 # Swallow it, we're seeing:
897 # error: (9, 'Bad file descriptor')
898 # when the listener channel is closed. Shouldn't be the case.
899 pass
Todd Fiala8cbeed32015-09-08 22:22:33 +0000900
901
902def inprocess_exec_test_runner(test_work_items):
903 # Initialize our global state.
904 initialize_global_vars_multiprocessing(1, test_work_items)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000905
Todd Fiala33896a92015-09-18 21:01:13 +0000906 # We're always worker index 0
907 global GET_WORKER_INDEX
908 GET_WORKER_INDEX = lambda: 0
909
Todd Fiala68615ce2015-09-15 21:38:04 +0000910 # Run the listener and related channel maps in a separate thread.
911 # global RUNNER_PROCESS_ASYNC_MAP
912 global RESULTS_LISTENER_CHANNEL
913 if RESULTS_LISTENER_CHANNEL is not None:
914 socket_thread = threading.Thread(
915 target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP))
916 socket_thread.start()
917
918 # Do the work.
919 test_results = map(process_dir_mapper_inprocess, test_work_items)
920
921 # If we have a listener channel, shut it down here.
922 if RESULTS_LISTENER_CHANNEL is not None:
923 # Close down the channel.
924 RESULTS_LISTENER_CHANNEL.close()
925 RESULTS_LISTENER_CHANNEL = None
926
927 # Wait for the listener and handlers to complete.
928 socket_thread.join()
929
930 return test_results
Todd Fiala8cbeed32015-09-08 22:22:33 +0000931
932def walk_and_invoke(test_directory, test_subdir, dotest_argv,
Todd Fiala871b2e52015-09-22 22:47:34 +0000933 num_workers, test_runner_func):
Steve Puccibefe2b12014-03-07 00:01:11 +0000934 """Look for matched files and invoke test driver on each one.
935 In single-threaded mode, each test driver is invoked directly.
936 In multi-threaded mode, submit each test driver to a worker
Vince Harrone06a7a82015-05-12 23:12:19 +0000937 queue, and then wait for all to complete.
938
939 test_directory - lldb/test/ directory
Chaoren Linb6325d02015-08-12 18:02:54 +0000940 test_subdir - lldb/test/ or a subfolder with the tests we're interested in
941 running
Vince Harrone06a7a82015-05-12 23:12:19 +0000942 """
Todd Fiala68615ce2015-09-15 21:38:04 +0000943 # The async_map is important to keep all thread-related asyncore
944 # channels distinct when we call asyncore.loop() later on.
945 global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP
946 RUNNER_PROCESS_ASYNC_MAP = {}
947
948 # If we're outputting side-channel test results, create the socket
949 # listener channel and tell the inferior to send results to the
950 # port on which we'll be listening.
951 if RESULTS_FORMATTER is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000952 forwarding_func = RESULTS_FORMATTER.handle_event
Todd Fiala68615ce2015-09-15 21:38:04 +0000953 RESULTS_LISTENER_CHANNEL = (
954 dotest_channels.UnpicklingForwardingListenerChannel(
Todd Fiala871b2e52015-09-22 22:47:34 +0000955 RUNNER_PROCESS_ASYNC_MAP, "localhost", 0,
956 2 * num_workers, forwarding_func))
Todd Fiala68615ce2015-09-15 21:38:04 +0000957 dotest_argv.append("--results-port")
958 dotest_argv.append(str(RESULTS_LISTENER_CHANNEL.address[1]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000959
960 # Collect the test files that we'll run.
961 test_work_items = []
Todd Fiala8cbeed32015-09-08 22:22:33 +0000962 find_test_files_in_dir_tree(
963 test_subdir, lambda testdir, test_files: test_work_items.append([
964 test_subdir, test_files, test_directory, dotest_argv, None]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000965
Todd Fiala8cbeed32015-09-08 22:22:33 +0000966 # Convert test work items into test results using whatever
967 # was provided as the test run function.
968 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +0000969
Todd Fiala8cbeed32015-09-08 22:22:33 +0000970 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +0000971 timed_out = sum([result[0] for result in test_results], [])
972 passed = sum([result[1] for result in test_results], [])
973 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +0000974 unexpected_successes = sum([result[3] for result in test_results], [])
975 pass_count = sum([result[4] for result in test_results])
976 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +0000977
Todd Fiala8cbeed32015-09-08 22:22:33 +0000978 return (timed_out, passed, failed, unexpected_successes, pass_count,
979 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +0000980
Chaoren Linb6325d02015-08-12 18:02:54 +0000981
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000982def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +0000983 # returns a set of test filenames that might timeout
984 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +0000985 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000986 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +0000987 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000988 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000989 m = re.search(r'remote-(\w+)', platform_name)
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000990 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +0000991
992 expected_timeout = set()
993
994 if target.startswith("linux"):
995 expected_timeout |= {
Vince Harron06381732015-05-12 23:10:36 +0000996 "TestConnectRemote.py",
997 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +0000998 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +0000999 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001000
1001 # Times out in ~10% of the times on the build bot
1002 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +00001003 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001004 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +00001005 "TestThreadStepOut.py",
1006 }
1007 elif target.startswith("android"):
1008 expected_timeout |= {
1009 "TestExitDuringStep.py",
1010 "TestHelloWorld.py",
1011 }
Chaoren Linfebef1b2015-08-19 17:22:12 +00001012 if host.startswith("win32"):
1013 expected_timeout |= {
1014 "TestEvents.py",
1015 "TestThreadStates.py",
1016 }
Ed Maste4dd8fba2015-05-14 16:25:52 +00001017 elif target.startswith("freebsd"):
1018 expected_timeout |= {
1019 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +00001020 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +00001021 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +00001022 "TestWatchpointConditionAPI.py",
1023 }
Vince Harron0f173ac2015-05-18 19:36:33 +00001024 elif target.startswith("darwin"):
1025 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +00001026 # times out on MBP Retina, Mid 2012
1027 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +00001028 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +00001029 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +00001030 }
Vince Harron06381732015-05-12 23:10:36 +00001031 return expected_timeout
1032
Chaoren Linb6325d02015-08-12 18:02:54 +00001033
Pavel Labathfad30cf2015-06-29 14:16:51 +00001034def getDefaultTimeout(platform_name):
1035 if os.getenv("LLDB_TEST_TIMEOUT"):
1036 return os.getenv("LLDB_TEST_TIMEOUT")
1037
1038 if platform_name is None:
1039 platform_name = sys.platform
1040
1041 if platform_name.startswith("remote-"):
1042 return "10m"
1043 else:
1044 return "4m"
1045
Chaoren Linb6325d02015-08-12 18:02:54 +00001046
Vince Harron0b9dbb52015-05-21 18:18:52 +00001047def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +00001048 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +00001049 os.utime(fname, times)
1050
Chaoren Linb6325d02015-08-12 18:02:54 +00001051
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001052def find(pattern, path):
1053 result = []
1054 for root, dirs, files in os.walk(path):
1055 for name in files:
1056 if fnmatch.fnmatch(name, pattern):
1057 result.append(os.path.join(root, name))
1058 return result
1059
Chaoren Linb6325d02015-08-12 18:02:54 +00001060
Todd Fiala8cbeed32015-09-08 22:22:33 +00001061def get_test_runner_strategies(num_threads):
1062 """Returns the test runner strategies by name in a dictionary.
1063
1064 @param num_threads specifies the number of threads/processes
1065 that will be used for concurrent test runners.
1066
1067 @return dictionary with key as test runner strategy name and
1068 value set to a callable object that takes the test work item
1069 and returns a test result tuple.
1070 """
1071 return {
1072 # multiprocessing supports ctrl-c and does not use
1073 # multiprocessing.Pool.
1074 "multiprocessing":
1075 (lambda work_items: multiprocessing_test_runner(
1076 num_threads, work_items)),
1077
1078 # multiprocessing-pool uses multiprocessing.Pool but
1079 # does not support Ctrl-C.
1080 "multiprocessing-pool":
1081 (lambda work_items: multiprocessing_test_runner_pool(
1082 num_threads, work_items)),
1083
1084 # threading uses a hand-rolled worker pool much
1085 # like multiprocessing, but instead uses in-process
1086 # worker threads. This one supports Ctrl-C.
1087 "threading":
1088 (lambda work_items: threading_test_runner(num_threads, work_items)),
1089
1090 # threading-pool uses threading for the workers (in-process)
1091 # and uses the multiprocessing.pool thread-enabled pool.
Todd Fiala68615ce2015-09-15 21:38:04 +00001092 # This does not properly support Ctrl-C.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001093 "threading-pool":
1094 (lambda work_items: threading_test_runner_pool(
1095 num_threads, work_items)),
1096
1097 # serial uses the subprocess-based, single process
1098 # test runner. This provides process isolation but
Todd Fiala68615ce2015-09-15 21:38:04 +00001099 # no concurrent test execution.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001100 "serial":
1101 inprocess_exec_test_runner
1102 }
1103
1104
Todd Fialaea736242015-09-23 15:21:28 +00001105def _remove_option(
1106 args, long_option_name, short_option_name, takes_arg):
Todd Fiala68615ce2015-09-15 21:38:04 +00001107 """Removes option and related option arguments from args array.
Todd Fialaea736242015-09-23 15:21:28 +00001108
1109 This method removes all short/long options that match the given
1110 arguments.
1111
Todd Fiala68615ce2015-09-15 21:38:04 +00001112 @param args the array of command line arguments (in/out)
Todd Fialaea736242015-09-23 15:21:28 +00001113
1114 @param long_option_name the full command line representation of the
1115 long-form option that will be removed (including '--').
1116
1117 @param short_option_name the short version of the command line option
1118 that will be removed (including '-').
1119
1120 @param takes_arg True if the option takes an argument.
1121
Todd Fiala68615ce2015-09-15 21:38:04 +00001122 """
Todd Fialaea736242015-09-23 15:21:28 +00001123 if long_option_name is not None:
1124 regex_string = "^" + long_option_name + "="
1125 long_regex = re.compile(regex_string)
1126 if short_option_name is not None:
1127 # Short options we only match the -X and assume
1128 # any arg is one command line argument jammed together.
1129 # i.e. -O--abc=1 is a single argument in the args list.
1130 # We don't handle -O --abc=1, as argparse doesn't handle
1131 # it, either.
1132 regex_string = "^" + short_option_name
1133 short_regex = re.compile(regex_string)
1134
1135 def remove_long_internal():
1136 """Removes one matching long option from args.
1137 @returns True if one was found and removed; False otherwise.
1138 """
1139 try:
1140 index = args.index(long_option_name)
1141 # Handle the exact match case.
1142 if takes_arg:
1143 removal_count = 2
1144 else:
1145 removal_count = 1
1146 del args[index:index+removal_count]
1147 return True
1148 except ValueError:
1149 # Thanks to argparse not handling options with known arguments
1150 # like other options parsing libraries (see
1151 # https://bugs.python.org/issue9334), we need to support the
1152 # --results-formatter-options={second-level-arguments} (note
1153 # the equal sign to fool the first-level arguments parser into
1154 # not treating the second-level arguments as first-level
1155 # options). We're certainly at risk of getting this wrong
1156 # since now we're forced into the business of trying to figure
1157 # out what is an argument (although I think this
1158 # implementation will suffice).
1159 for index in range(len(args)):
1160 match = long_regex.search(args[index])
1161 if match:
1162 del args[index]
1163 return True
1164 return False
1165
1166 def remove_short_internal():
1167 """Removes one matching short option from args.
1168 @returns True if one was found and removed; False otherwise.
1169 """
Todd Fiala68615ce2015-09-15 21:38:04 +00001170 for index in range(len(args)):
Todd Fialaea736242015-09-23 15:21:28 +00001171 match = short_regex.search(args[index])
Todd Fiala68615ce2015-09-15 21:38:04 +00001172 if match:
Todd Fiala68615ce2015-09-15 21:38:04 +00001173 del args[index]
Todd Fialaea736242015-09-23 15:21:28 +00001174 return True
1175 return False
Todd Fiala68615ce2015-09-15 21:38:04 +00001176
Todd Fialaea736242015-09-23 15:21:28 +00001177 removal_count = 0
1178 while long_option_name is not None and remove_long_internal():
1179 removal_count += 1
1180 while short_option_name is not None and remove_short_internal():
1181 removal_count += 1
1182 if removal_count == 0:
1183 raise Exception(
1184 "failed to find at least one of '{}', '{}' in options".format(
1185 long_option_name, short_option_name))
Todd Fiala68615ce2015-09-15 21:38:04 +00001186
1187
1188def adjust_inferior_options(dotest_argv):
1189 """Adjusts the commandline args array for inferiors.
1190
1191 This method adjusts the inferior dotest commandline options based
1192 on the parallel test runner's options. Some of the inferior options
1193 will need to change to properly handle aggregation functionality.
1194 """
1195 global dotest_options
1196
1197 # If we don't have a session directory, create one.
1198 if not dotest_options.s:
1199 # no session log directory, we need to add this to prevent
1200 # every dotest invocation from creating its own directory
1201 import datetime
1202 # The windows platforms don't like ':' in the pathname.
1203 timestamp_started = datetime.datetime.now().strftime("%F-%H_%M_%S")
1204 dotest_argv.append('-s')
1205 dotest_argv.append(timestamp_started)
1206 dotest_options.s = timestamp_started
1207
1208 # Adjust inferior results formatter options - if the parallel
1209 # test runner is collecting into the user-specified test results,
1210 # we'll have inferiors spawn with the --results-port option and
1211 # strip the original test runner options.
1212 if dotest_options.results_file is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001213 _remove_option(dotest_argv, "--results-file", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001214 if dotest_options.results_port is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001215 _remove_option(dotest_argv, "--results-port", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001216 if dotest_options.results_formatter is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001217 _remove_option(dotest_argv, "--results-formatter", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001218 if dotest_options.results_formatter_options is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001219 _remove_option(dotest_argv, "--results-formatter-option", "-O",
1220 True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001221
Todd Fiala33896a92015-09-18 21:01:13 +00001222 # Remove test runner name if present.
1223 if dotest_options.test_runner_name is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001224 _remove_option(dotest_argv, "--test-runner-name", None, True)
Todd Fiala33896a92015-09-18 21:01:13 +00001225
1226
Todd Fiala83c32e32015-09-22 21:19:40 +00001227def is_darwin_version_lower_than(target_version):
1228 """Checks that os is Darwin and version is lower than target_version.
1229
1230 @param target_version the StrictVersion indicating the version
1231 we're checking against.
1232
1233 @return True if the OS is Darwin (OS X) and the version number of
1234 the OS is less than target_version; False in all other cases.
1235 """
1236 if platform.system() != 'Darwin':
1237 # Can't be Darwin lower than a certain version.
1238 return False
1239
1240 system_version = distutils.version.StrictVersion(platform.mac_ver()[0])
Todd Fiala871b2e52015-09-22 22:47:34 +00001241 return cmp(system_version, target_version) < 0
Todd Fiala83c32e32015-09-22 21:19:40 +00001242
1243
1244def default_test_runner_name(num_threads):
1245 """Returns the default test runner name for the configuration.
1246
1247 @param num_threads the number of threads/workers this test runner is
1248 supposed to use.
1249
1250 @return the test runner name that should be used by default when
1251 no test runner was explicitly called out on the command line.
1252 """
1253 if num_threads == 1:
1254 # Use the serial runner.
1255 test_runner_name = "serial"
1256 elif os.name == "nt":
Adrian McCarthy040b31d2015-10-12 14:46:57 +00001257 # On Windows, Python uses CRT with a low limit on the number of open
1258 # files. If you have a lot of cores, the threading-pool runner will
1259 # often fail because it exceeds that limit.
1260 if num_threads > 32:
1261 test_runner_name = "multiprocessing-pool"
1262 else:
1263 test_runner_name = "threading-pool"
Todd Fiala83c32e32015-09-22 21:19:40 +00001264 elif is_darwin_version_lower_than(
1265 distutils.version.StrictVersion("10.10.0")):
1266 # OS X versions before 10.10 appear to have an issue using
1267 # the threading test runner. Fall back to multiprocessing.
1268 # Supports Ctrl-C.
1269 test_runner_name = "multiprocessing"
1270 else:
1271 # For everyone else, use the ctrl-c-enabled threading support.
1272 # Should use fewer system resources than the multprocessing
1273 # variant.
1274 test_runner_name = "threading"
1275 return test_runner_name
1276
1277
Todd Fiala8cbeed32015-09-08 22:22:33 +00001278def main(print_details_on_success, num_threads, test_subdir,
Todd Fiala68615ce2015-09-15 21:38:04 +00001279 test_runner_name, results_formatter):
Todd Fialafed95662015-09-03 18:58:44 +00001280 """Run dotest.py in inferior mode in parallel.
1281
1282 @param print_details_on_success the parsed value of the output-on-success
1283 command line argument. When True, details of a successful dotest inferior
1284 are printed even when everything succeeds. The normal behavior is to
1285 not print any details when all the inferior tests pass.
1286
1287 @param num_threads the parsed value of the num-threads command line
1288 argument.
1289
1290 @param test_subdir optionally specifies a subdir to limit testing
1291 within. May be None if the entire test tree is to be used. This subdir
1292 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001293
1294 @param test_runner_name if specified, contains the test runner
1295 name which selects the strategy used to run the isolated and
1296 optionally concurrent test runner. Specify None to allow the
1297 system to choose the most appropriate test runner given desired
1298 thread count and OS type.
1299
Todd Fiala68615ce2015-09-15 21:38:04 +00001300 @param results_formatter if specified, provides the TestResultsFormatter
1301 instance that will format and output test result data from the
1302 side-channel test results. When specified, inferior dotest calls
1303 will send test results side-channel data over a socket to the parallel
1304 test runner, which will forward them on to results_formatter.
Todd Fialafed95662015-09-03 18:58:44 +00001305 """
1306
Todd Fiala1cc97b42015-09-21 05:42:26 +00001307 # Do not shut down on sighup.
Ying Chend93aa102015-09-23 21:53:18 +00001308 if hasattr(signal, 'SIGHUP'):
1309 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala1cc97b42015-09-21 05:42:26 +00001310
Todd Fialafed95662015-09-03 18:58:44 +00001311 dotest_argv = sys.argv[1:]
1312
Greg Claytonb0d148e2015-09-21 17:25:01 +00001313 global output_on_success, RESULTS_FORMATTER
Todd Fialafed95662015-09-03 18:58:44 +00001314 output_on_success = print_details_on_success
Todd Fiala68615ce2015-09-15 21:38:04 +00001315 RESULTS_FORMATTER = results_formatter
Todd Fialafed95662015-09-03 18:58:44 +00001316
Vince Harrond5fa1022015-05-10 15:24:12 +00001317 # We can't use sys.path[0] to determine the script directory
1318 # because it doesn't work under a debugger
Vince Harron8994fed2015-05-22 19:49:23 +00001319 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +00001320 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +00001321 dotest_options = dotest_args.parse_args(parser, dotest_argv)
1322
Todd Fiala68615ce2015-09-15 21:38:04 +00001323 adjust_inferior_options(dotest_argv)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001324
1325 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +00001326
Vince Harrone06a7a82015-05-12 23:12:19 +00001327 # The root directory was specified on the command line
Todd Fiala68615ce2015-09-15 21:38:04 +00001328 test_directory = os.path.dirname(os.path.realpath(__file__))
Todd Fialafed95662015-09-03 18:58:44 +00001329 if test_subdir and len(test_subdir) > 0:
1330 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +00001331 else:
Todd Fialafed95662015-09-03 18:58:44 +00001332 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +00001333
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001334 # clean core files in test tree from previous runs (Linux)
1335 cores = find('core.*', test_subdir)
1336 for core in cores:
1337 os.unlink(core)
1338
Daniel Maleab42556f2013-04-19 18:32:53 +00001339 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +00001340
1341 # Figure out which testrunner strategy we'll use.
1342 runner_strategies_by_name = get_test_runner_strategies(num_threads)
1343
1344 # If the user didn't specify a test runner strategy, determine
1345 # the default now based on number of threads and OS type.
1346 if not test_runner_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001347 test_runner_name = default_test_runner_name(num_threads)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001348
1349 if test_runner_name not in runner_strategies_by_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001350 raise Exception(
1351 "specified testrunner name '{}' unknown. Valid choices: {}".format(
1352 test_runner_name,
1353 runner_strategies_by_name.keys()))
Todd Fiala8cbeed32015-09-08 22:22:33 +00001354 test_runner_func = runner_strategies_by_name[test_runner_name]
1355
1356 summary_results = walk_and_invoke(
Todd Fiala871b2e52015-09-22 22:47:34 +00001357 test_directory, test_subdir, dotest_argv,
1358 num_threads, test_runner_func)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001359
1360 (timed_out, passed, failed, unexpected_successes, pass_count,
1361 fail_count) = summary_results
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +00001362
Todd Fialade9a44e2015-09-22 00:15:50 +00001363 # The results formatter - if present - is done now. Tell it to
1364 # terminate.
1365 if results_formatter is not None:
1366 results_formatter.send_terminate_as_needed()
1367
Vince Harron17f429f2014-12-13 00:08:19 +00001368 timed_out = set(timed_out)
Chaoren Line80372a2015-08-12 18:02:53 +00001369 num_test_files = len(passed) + len(failed)
1370 num_test_cases = pass_count + fail_count
Daniel Maleab42556f2013-04-19 18:32:53 +00001371
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001372 # move core files into session dir
1373 cores = find('core.*', test_subdir)
1374 for core in cores:
1375 dst = core.replace(test_directory, "")[1:]
1376 dst = dst.replace(os.path.sep, "-")
1377 os.rename(core, os.path.join(session_dir, dst))
1378
Vince Harron06381732015-05-12 23:10:36 +00001379 # remove expected timeouts from failures
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001380 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
Vince Harron06381732015-05-12 23:10:36 +00001381 for xtime in expected_timeout:
1382 if xtime in timed_out:
1383 timed_out.remove(xtime)
1384 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001385 result = "ExpectedTimeout"
1386 elif xtime in passed:
1387 result = "UnexpectedCompletion"
1388 else:
1389 result = None # failed
1390
1391 if result:
1392 test_name = os.path.splitext(xtime)[0]
1393 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +00001394
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +00001395 print
Chaoren Lin5a59e462015-08-12 18:02:51 +00001396 sys.stdout.write("Ran %d test suites" % num_test_files)
1397 if num_test_files > 0:
1398 sys.stdout.write(" (%d failed) (%f%%)" % (
1399 len(failed), 100.0 * len(failed) / num_test_files))
1400 print
Chaoren Line80372a2015-08-12 18:02:53 +00001401 sys.stdout.write("Ran %d test cases" % num_test_cases)
1402 if num_test_cases > 0:
Chaoren Lin5a59e462015-08-12 18:02:51 +00001403 sys.stdout.write(" (%d failed) (%f%%)" % (
Chaoren Line80372a2015-08-12 18:02:53 +00001404 fail_count, 100.0 * fail_count / num_test_cases))
Chaoren Lin5a59e462015-08-12 18:02:51 +00001405 print
Zachary Turner4cceca72015-08-14 16:45:32 +00001406 exit_code = 0
1407
Daniel Maleacbaef262013-02-15 21:31:37 +00001408 if len(failed) > 0:
Shawn Best13491c42014-10-22 19:29:00 +00001409 failed.sort()
Ying Chen10ed1a92015-05-28 23:51:49 +00001410 print "Failing Tests (%d)" % len(failed)
Daniel Maleacbaef262013-02-15 21:31:37 +00001411 for f in failed:
Vince Harron17f429f2014-12-13 00:08:19 +00001412 print "%s: LLDB (suite) :: %s (%s)" % (
1413 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
1414 )
Zachary Turner4cceca72015-08-14 16:45:32 +00001415 exit_code = 1
1416
1417 if len(unexpected_successes) > 0:
1418 unexpected_successes.sort()
1419 print "\nUnexpected Successes (%d)" % len(unexpected_successes)
1420 for u in unexpected_successes:
1421 print "UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info)
1422
1423 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001424
1425if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +00001426 sys.stderr.write(
1427 "error: dosep.py no longer supports being called directly. "
1428 "Please call dotest.py directly. The dosep.py-specific arguments "
1429 "have been added under the Parallel processing arguments.\n")
1430 sys.exit(128)