blob: 7be5c82271a2b40f0a952ee8b77cd4cb97e2dc41 [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
Vince Harrondcc2b9f2015-05-27 04:40:36 +000035import fnmatch
Todd Fiala8cbeed32015-09-08 22:22:33 +000036import multiprocessing
37import multiprocessing.pool
38import os
Todd Fiala3f0a3602014-07-08 06:42:37 +000039import platform
Todd Fiala8cbeed32015-09-08 22:22:33 +000040import Queue
Vince Harron06381732015-05-12 23:10:36 +000041import re
Todd Fiala8cbeed32015-09-08 22:22:33 +000042import signal
Vince Harron17f429f2014-12-13 00:08:19 +000043import subprocess
Todd Fiala3f0a3602014-07-08 06:42:37 +000044import sys
Todd Fiala8cbeed32015-09-08 22:22:33 +000045import threading
46
47import dotest_args
Steve Puccibefe2b12014-03-07 00:01:11 +000048
Johnny Chene8d9dc62011-10-31 19:04:07 +000049from optparse import OptionParser
50
Chaoren Linb6325d02015-08-12 18:02:54 +000051
Vince Harron17f429f2014-12-13 00:08:19 +000052def get_timeout_command():
Vince Harronede59652015-01-08 02:11:26 +000053 """Search for a suitable timeout command."""
Ying Chen93190c42015-07-20 20:04:22 +000054 if not sys.platform.startswith("win32"):
55 try:
56 subprocess.call("timeout", stderr=subprocess.PIPE)
57 return "timeout"
58 except OSError:
59 pass
Vince Harron17f429f2014-12-13 00:08:19 +000060 try:
Chaoren Lin45c17ff2015-05-28 23:00:10 +000061 subprocess.call("gtimeout", stderr=subprocess.PIPE)
Vince Harron17f429f2014-12-13 00:08:19 +000062 return "gtimeout"
63 except OSError:
64 pass
65 return None
66
67timeout_command = get_timeout_command()
68
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
Zachary Turner38e64172015-08-10 17:46:11 +000077output_on_success = False
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000078
Chaoren Linb6325d02015-08-12 18:02:54 +000079
Chaoren Linffc63b02015-08-12 18:02:49 +000080def setup_global_variables(lock, counter, total, name_len, options):
81 global output_lock, test_counter, total_tests, test_name_len
82 global dotest_options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000083 output_lock = lock
84 test_counter = counter
85 total_tests = total
Chaoren Linffc63b02015-08-12 18:02:49 +000086 test_name_len = name_len
Pavel Labath05ab2372015-07-06 15:57:52 +000087 dotest_options = options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000088
Chaoren Linb6325d02015-08-12 18:02:54 +000089
Zachary Turner38e64172015-08-10 17:46:11 +000090def report_test_failure(name, command, output):
91 global output_lock
92 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +000093 print >> sys.stderr
Zachary Turner38e64172015-08-10 17:46:11 +000094 print >> sys.stderr, output
Chaoren Linffc63b02015-08-12 18:02:49 +000095 print >> sys.stderr, "[%s FAILED]" % name
Zachary Turner38e64172015-08-10 17:46:11 +000096 print >> sys.stderr, "Command invoked: %s" % ' '.join(command)
Chaoren Linffc63b02015-08-12 18:02:49 +000097 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +000098
Chaoren Linb6325d02015-08-12 18:02:54 +000099
Zachary Turner38e64172015-08-10 17:46:11 +0000100def report_test_pass(name, output):
101 global output_lock, output_on_success
102 with output_lock:
103 if output_on_success:
Chaoren Linffc63b02015-08-12 18:02:49 +0000104 print >> sys.stderr
Zachary Turner38e64172015-08-10 17:46:11 +0000105 print >> sys.stderr, output
Chaoren Linffc63b02015-08-12 18:02:49 +0000106 print >> sys.stderr, "[%s PASSED]" % name
107 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000108
Chaoren Linb6325d02015-08-12 18:02:54 +0000109
Chaoren Linffc63b02015-08-12 18:02:49 +0000110def update_progress(test_name=""):
111 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000112 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000113 counter_len = len(str(total_tests))
114 sys.stderr.write(
115 "\r%*d out of %d test suites processed - %-*s" %
116 (counter_len, test_counter.value, total_tests,
117 test_name_len.value, test_name))
118 if len(test_name) > test_name_len.value:
119 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000120 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000121 sys.stdout.flush()
122 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000123
Chaoren Linb6325d02015-08-12 18:02:54 +0000124
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000125def parse_test_results(output):
126 passes = 0
127 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000128 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000129 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000130 pass_count = re.search("^RESULT:.*([0-9]+) passes",
131 result, re.MULTILINE)
132 fail_count = re.search("^RESULT:.*([0-9]+) failures",
133 result, re.MULTILINE)
134 error_count = re.search("^RESULT:.*([0-9]+) errors",
135 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000136 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
137 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000138 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000139 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000140 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000141 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000142 if unexpected_success_count is not None:
143 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000144 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000145 failures = failures + int(error_count.group(1))
146 pass
Zachary Turner4cceca72015-08-14 16:45:32 +0000147 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000148
Chaoren Linb6325d02015-08-12 18:02:54 +0000149
Todd Fiala8cbeed32015-09-08 22:22:33 +0000150def call_with_timeout(command, timeout, name, inferior_pid_events):
Vince Harronede59652015-01-08 02:11:26 +0000151 """Run command with a timeout if possible."""
Vince Harrondcc2b9f2015-05-27 04:40:36 +0000152 """-s QUIT will create a coredump if they are enabled on your system"""
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000153 process = None
154 if timeout_command and timeout != "0":
155 command = [timeout_command, '-s', 'QUIT', timeout] + command
Chaoren Linb6325d02015-08-12 18:02:54 +0000156 # Specifying a value for close_fds is unsupported on Windows when using
157 # subprocess.PIPE
Zachary Turnerdc494d52015-02-07 00:14:55 +0000158 if os.name != "nt":
Chaoren Linb6325d02015-08-12 18:02:54 +0000159 process = subprocess.Popen(command,
160 stdin=subprocess.PIPE,
161 stdout=subprocess.PIPE,
162 stderr=subprocess.PIPE,
163 close_fds=True)
Zachary Turnerdc494d52015-02-07 00:14:55 +0000164 else:
Chaoren Linb6325d02015-08-12 18:02:54 +0000165 process = subprocess.Popen(command,
166 stdin=subprocess.PIPE,
167 stdout=subprocess.PIPE,
168 stderr=subprocess.PIPE)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000169 inferior_pid = process.pid
170 if inferior_pid_events:
171 inferior_pid_events.put_nowait(('created', inferior_pid))
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000172 output = process.communicate()
173 exit_status = process.returncode
Todd Fiala8cbeed32015-09-08 22:22:33 +0000174 if inferior_pid_events:
175 inferior_pid_events.put_nowait(('destroyed', inferior_pid))
176
Zachary Turner4cceca72015-08-14 16:45:32 +0000177 passes, failures, unexpected_successes = parse_test_results(output)
Zachary Turner38e64172015-08-10 17:46:11 +0000178 if exit_status == 0:
Chaoren Linb6325d02015-08-12 18:02:54 +0000179 # stdout does not have any useful information from 'dotest.py',
180 # only stderr does.
Zachary Turner38e64172015-08-10 17:46:11 +0000181 report_test_pass(name, output[1])
182 else:
183 report_test_failure(name, command, output[1])
Zachary Turner4cceca72015-08-14 16:45:32 +0000184 return name, exit_status, passes, failures, unexpected_successes
Johnny Chene8d9dc62011-10-31 19:04:07 +0000185
Chaoren Linb6325d02015-08-12 18:02:54 +0000186
Todd Fiala8cbeed32015-09-08 22:22:33 +0000187def process_dir(root, files, test_root, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000188 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000189 results = []
Steve Puccibefe2b12014-03-07 00:01:11 +0000190 for name in files:
Zachary Turnerf6896b02015-01-05 19:37:03 +0000191 script_file = os.path.join(test_root, "dotest.py")
Zachary Turnerf6896b02015-01-05 19:37:03 +0000192 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000193 dotest_argv +
Todd Fialafed95662015-09-03 18:58:44 +0000194 ["--inferior", "-p", name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000195
196 timeout_name = os.path.basename(os.path.splitext(name)[0]).upper()
197
Chaoren Linb6325d02015-08-12 18:02:54 +0000198 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
199 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000200
Todd Fiala8cbeed32015-09-08 22:22:33 +0000201 results.append(call_with_timeout(
202 command, timeout, name, inferior_pid_events))
Vince Harron17f429f2014-12-13 00:08:19 +0000203
Zachary Turner4cceca72015-08-14 16:45:32 +0000204 # result = (name, status, passes, failures, unexpected_successes)
205 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000206 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000207 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000208 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000209 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000210 if status != ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000211 unexpected_passes = [name for name, _, _, _, unexpected_successes in results
212 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000213
Chaoren Line80372a2015-08-12 18:02:53 +0000214 pass_count = sum([result[2] for result in results])
215 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000216
Zachary Turner4cceca72015-08-14 16:45:32 +0000217 return (timed_out, passed, failed, unexpected_passes, pass_count, fail_count)
Steve Puccibefe2b12014-03-07 00:01:11 +0000218
219in_q = None
220out_q = None
221
Chaoren Linb6325d02015-08-12 18:02:54 +0000222
Todd Fiala8cbeed32015-09-08 22:22:33 +0000223def process_dir_worker_multiprocessing(
224 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
225 a_dotest_options, job_queue, result_queue, inferior_pid_events):
226 """Worker thread main loop when in multiprocessing mode.
Steve Puccibefe2b12014-03-07 00:01:11 +0000227 Takes one directory specification at a time and works on it."""
Todd Fiala8cbeed32015-09-08 22:22:33 +0000228
229 # Shut off interrupt handling in the child process.
230 signal.signal(signal.SIGINT, signal.SIG_IGN)
231
232 # Setup the global state for the worker process.
233 setup_global_variables(
234 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
235 a_dotest_options)
236
237 # Keep grabbing entries from the queue until done.
238 while not job_queue.empty():
239 try:
240 job = job_queue.get(block=False)
241 result = process_dir(job[0], job[1], job[2], job[3],
242 inferior_pid_events)
243 result_queue.put(result)
244 except Queue.Empty:
245 # Fine, we're done.
246 pass
Steve Puccibefe2b12014-03-07 00:01:11 +0000247
Chaoren Linb6325d02015-08-12 18:02:54 +0000248
Todd Fiala8cbeed32015-09-08 22:22:33 +0000249def process_dir_worker_multiprocessing_pool(args):
250 return process_dir(*args)
251
252
253def process_dir_worker_threading(
254 a_test_counter, a_total_tests, a_test_name_len,
255 a_dotest_options, job_queue, result_queue, inferior_pid_events):
256 """Worker thread main loop when in threading mode.
257
258 This one supports the hand-rolled pooling support.
259
260 Takes one directory specification at a time and works on it."""
261
262 # Keep grabbing entries from the queue until done.
263 while not job_queue.empty():
264 try:
265 job = job_queue.get(block=False)
266 result = process_dir(job[0], job[1], job[2], job[3],
267 inferior_pid_events)
268 result_queue.put(result)
269 except Queue.Empty:
270 # Fine, we're done.
271 pass
272
273
274def process_dir_worker_threading_pool(args):
275 return process_dir(*args)
276
277
278def process_dir_mapper_inprocess(args):
279 """Map adapter for running the subprocess-based, non-threaded test runner.
280
281 @param args the process work item tuple
282 @return the test result tuple
283 """
284 return process_dir(*args)
285
286
287def collect_active_pids_from_pid_events(event_queue):
288 """
289 Returns the set of what should be active inferior pids based on
290 the event stream.
291
292 @param event_queue a multiprocessing.Queue containing events of the
293 form:
294 ('created', pid)
295 ('destroyed', pid)
296
297 @return set of inferior dotest.py pids activated but never completed.
298 """
299 active_pid_set = set()
300 while not event_queue.empty():
301 pid_event = event_queue.get_nowait()
302 if pid_event[0] == 'created':
303 active_pid_set.add(pid_event[1])
304 elif pid_event[0] == 'destroyed':
305 active_pid_set.remove(pid_event[1])
306 return active_pid_set
307
308
309def kill_all_worker_processes(workers, inferior_pid_events):
310 """
311 Kills all specified worker processes and their process tree.
312
313 @param workers a list of multiprocess.Process worker objects.
314 @param inferior_pid_events a multiprocess.Queue that contains
315 all inferior create and destroy events. Used to construct
316 the list of child pids still outstanding that need to be killed.
317 """
318 for worker in workers:
319 worker.terminate()
320 worker.join()
321
322 # Add all the child test pids created.
323 active_pid_set = collect_active_pids_from_pid_events(
324 inferior_pid_events)
325 for inferior_pid in active_pid_set:
326 print "killing inferior pid {}".format(inferior_pid)
327 os.kill(inferior_pid, signal.SIGKILL)
328
329
330def kill_all_worker_threads(workers, inferior_pid_events):
331 """
332 Kills all specified worker threads and their process tree.
333
334 @param workers a list of multiprocess.Process worker objects.
335 @param inferior_pid_events a multiprocess.Queue that contains
336 all inferior create and destroy events. Used to construct
337 the list of child pids still outstanding that need to be killed.
338 """
339
340 # Add all the child test pids created.
341 active_pid_set = collect_active_pids_from_pid_events(
342 inferior_pid_events)
343 for inferior_pid in active_pid_set:
344 print "killing inferior pid {}".format(inferior_pid)
345 os.kill(inferior_pid, signal.SIGKILL)
346
347 # We don't have a way to nuke the threads. However, since we killed
348 # all the inferiors, and we drained the job queue, this will be
349 # good enough. Wait cleanly for each worker thread to wrap up.
350 for worker in workers:
351 worker.join()
352
353
354def find_test_files_in_dir_tree(dir_root, found_func):
355 """Calls found_func for all the test files in the given dir hierarchy.
356
357 @param dir_root the path to the directory to start scanning
358 for test files. All files in this directory and all its children
359 directory trees will be searched.
360
361 @param found_func a callable object that will be passed
362 the parent directory (relative to dir_root) and the list of
363 test files from within that directory.
364 """
365 for root, _, files in os.walk(dir_root, topdown=False):
366 def is_test_filename(test_dir, base_filename):
367 """Returns True if the given filename matches the test name format.
368
369 @param test_dir the directory to check. Should be absolute or
370 relative to current working directory.
371
372 @param base_filename the base name of the filename to check for a
373 dherence to the python test case filename format.
374
375 @return True if name matches the python test case filename format.
376 """
377 # Not interested in symbolically linked files.
378 if os.path.islink(os.path.join(test_dir, base_filename)):
379 return False
380 # Only interested in test files with the "Test*.py" naming pattern.
381 return (base_filename.startswith("Test") and
382 base_filename.endswith(".py"))
383
384 tests = [filename for filename in files
385 if is_test_filename(root, filename)]
386 if tests:
387 found_func(root, tests)
388
389
390def initialize_global_vars_common(num_threads, test_work_items):
391 global total_tests, test_counter, test_name_len
392 total_tests = sum([len(item[1]) for item in test_work_items])
393 test_counter = multiprocessing.Value('i', 0)
394 test_name_len = multiprocessing.Value('i', 0)
395 print >> sys.stderr, "Testing: %d test suites, %d thread%s" % (
396 total_tests, num_threads, (num_threads > 1) * "s")
397 update_progress()
398
399
400def initialize_global_vars_multiprocessing(num_threads, test_work_items):
401 # Initialize the global state we'll use to communicate with the
402 # rest of the flat module.
403 global output_lock
404 output_lock = multiprocessing.RLock()
405 initialize_global_vars_common(num_threads, test_work_items)
406
407
408def initialize_global_vars_threading(num_threads, test_work_items):
409 # Initialize the global state we'll use to communicate with the
410 # rest of the flat module.
411 global output_lock
412 output_lock = threading.RLock()
413 initialize_global_vars_common(num_threads, test_work_items)
414
415
416def multiprocessing_test_runner(num_threads, test_work_items):
417 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
418
419 This concurrent test runner is based on the multiprocessing
420 library, and rolls its own worker pooling strategy so it
421 can handle Ctrl-C properly.
422
423 This test runner is known to have an issue running on
424 Windows platforms.
425
426 @param num_threads the number of worker processes to use.
427
428 @param test_work_items the iterable of test work item tuples
429 to run.
430 """
431
432 # Initialize our global state.
433 initialize_global_vars_multiprocessing(num_threads, test_work_items)
434
435 # Create jobs.
436 job_queue = multiprocessing.Queue(len(test_work_items))
437 for test_work_item in test_work_items:
438 job_queue.put(test_work_item)
439
440 result_queue = multiprocessing.Queue(len(test_work_items))
441
442 # Create queues for started child pids. Terminating
443 # the multiprocess processes does not terminate the
444 # child processes they spawn. We can remove this tracking
445 # if/when we move to having the multiprocess process directly
446 # perform the test logic. The Queue size needs to be able to
447 # hold 2 * (num inferior dotest.py processes started) entries.
448 inferior_pid_events = multiprocessing.Queue(4096)
449
450 # Create workers. We don't use multiprocessing.Pool due to
451 # challenges with handling ^C keyboard interrupts.
452 workers = []
453 for _ in range(num_threads):
454 worker = multiprocessing.Process(
455 target=process_dir_worker_multiprocessing,
456 args=(output_lock,
457 test_counter,
458 total_tests,
459 test_name_len,
460 dotest_options,
461 job_queue,
462 result_queue,
463 inferior_pid_events))
464 worker.start()
465 workers.append(worker)
466
467 # Wait for all workers to finish, handling ^C as needed.
468 try:
469 for worker in workers:
470 worker.join()
471 except KeyboardInterrupt:
472 # First try to drain the queue of work and let the
473 # running tests complete.
474 while not job_queue.empty():
475 try:
476 # Just drain it to stop more work from being started.
477 job_queue.get_nowait()
478 except Queue.Empty:
479 pass
480
481 print ('\nFirst KeyboardInterrupt received, stopping '
482 'future work. Press again to hard-stop existing tests.')
483 try:
484 for worker in workers:
485 worker.join()
486 except KeyboardInterrupt:
487 print ('\nSecond KeyboardInterrupt received, killing '
488 'all worker process trees.')
489 kill_all_worker_processes(workers, inferior_pid_events)
490
491 test_results = []
492 while not result_queue.empty():
493 test_results.append(result_queue.get(block=False))
494 return test_results
495
496
497def multiprocessing_test_runner_pool(num_threads, test_work_items):
498 # Initialize our global state.
499 initialize_global_vars_multiprocessing(num_threads, test_work_items)
500
501 pool = multiprocessing.Pool(
502 num_threads,
503 initializer=setup_global_variables,
504 initargs=(output_lock, test_counter, total_tests, test_name_len,
505 dotest_options))
506 return pool.map(process_dir_worker_multiprocessing_pool, test_work_items)
507
508
509def threading_test_runner(num_threads, test_work_items):
510 """Provides hand-wrapped pooling threading-based test runner adapter
511 with Ctrl-C support.
512
513 This concurrent test runner is based on the threading
514 library, and rolls its own worker pooling strategy so it
515 can handle Ctrl-C properly.
516
517 @param num_threads the number of worker processes to use.
518
519 @param test_work_items the iterable of test work item tuples
520 to run.
521 """
522
523 # Initialize our global state.
524 initialize_global_vars_threading(num_threads, test_work_items)
525
526 # Create jobs.
527 job_queue = Queue.Queue()
528 for test_work_item in test_work_items:
529 job_queue.put(test_work_item)
530
531 result_queue = Queue.Queue()
532
533 # Create queues for started child pids. Terminating
534 # the threading threads does not terminate the
535 # child processes they spawn.
536 inferior_pid_events = Queue.Queue()
537
538 # Create workers. We don't use multiprocessing.pool.ThreadedPool
539 # due to challenges with handling ^C keyboard interrupts.
540 workers = []
541 for _ in range(num_threads):
542 worker = threading.Thread(
543 target=process_dir_worker_threading,
544 args=(test_counter,
545 total_tests,
546 test_name_len,
547 dotest_options,
548 job_queue,
549 result_queue,
550 inferior_pid_events))
551 worker.start()
552 workers.append(worker)
553
554 # Wait for all workers to finish, handling ^C as needed.
555 try:
556 # We do some trickery here to ensure we can catch keyboard
557 # interrupts.
558 while len(workers) > 0:
559 # Make a pass throug the workers, checking for who is done.
560 dead_workers = []
561 for worker in workers:
562 # This non-blocking join call is what allows us
563 # to still receive keyboard interrupts.
564 worker.join(0.01)
565 if not worker.isAlive():
566 dead_workers.append(worker)
567 # Clear out the completed workers
568 for dead_worker in dead_workers:
569 workers.remove(dead_worker)
570
571 except KeyboardInterrupt:
572 # First try to drain the queue of work and let the
573 # running tests complete.
574 while not job_queue.empty():
575 try:
576 # Just drain it to stop more work from being started.
577 job_queue.get_nowait()
578 except Queue.Empty:
579 pass
580
581 print ('\nFirst KeyboardInterrupt received, stopping '
582 'future work. Press again to hard-stop existing tests.')
583 try:
584 for worker in workers:
585 worker.join()
586 except KeyboardInterrupt:
587 print ('\nSecond KeyboardInterrupt received, killing '
588 'all worker process trees.')
589 kill_all_worker_threads(workers, inferior_pid_events)
590
591 test_results = []
592 while not result_queue.empty():
593 test_results.append(result_queue.get(block=False))
594 return test_results
595
596
597def threading_test_runner_pool(num_threads, test_work_items):
598 # Initialize our global state.
599 initialize_global_vars_threading(num_threads, test_work_items)
600
601 pool = multiprocessing.pool.ThreadPool(
602 num_threads
603 # initializer=setup_global_variables,
604 # initargs=(output_lock, test_counter, total_tests, test_name_len,
605 # dotest_options)
606 )
607 return pool.map(process_dir_worker_threading_pool, test_work_items)
608
609
610def inprocess_exec_test_runner(test_work_items):
611 # Initialize our global state.
612 initialize_global_vars_multiprocessing(1, test_work_items)
613 return map(process_dir_mapper_inprocess, test_work_items)
614
615
616def walk_and_invoke(test_directory, test_subdir, dotest_argv,
617 test_runner_func):
Steve Puccibefe2b12014-03-07 00:01:11 +0000618 """Look for matched files and invoke test driver on each one.
619 In single-threaded mode, each test driver is invoked directly.
620 In multi-threaded mode, submit each test driver to a worker
Vince Harrone06a7a82015-05-12 23:12:19 +0000621 queue, and then wait for all to complete.
622
623 test_directory - lldb/test/ directory
Chaoren Linb6325d02015-08-12 18:02:54 +0000624 test_subdir - lldb/test/ or a subfolder with the tests we're interested in
625 running
Vince Harrone06a7a82015-05-12 23:12:19 +0000626 """
Todd Fiala3f0a3602014-07-08 06:42:37 +0000627
628 # Collect the test files that we'll run.
629 test_work_items = []
Todd Fiala8cbeed32015-09-08 22:22:33 +0000630 find_test_files_in_dir_tree(
631 test_subdir, lambda testdir, test_files: test_work_items.append([
632 test_subdir, test_files, test_directory, dotest_argv, None]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000633
Todd Fiala8cbeed32015-09-08 22:22:33 +0000634 # Convert test work items into test results using whatever
635 # was provided as the test run function.
636 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +0000637
Todd Fiala8cbeed32015-09-08 22:22:33 +0000638 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +0000639 timed_out = sum([result[0] for result in test_results], [])
640 passed = sum([result[1] for result in test_results], [])
641 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +0000642 unexpected_successes = sum([result[3] for result in test_results], [])
643 pass_count = sum([result[4] for result in test_results])
644 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +0000645
Todd Fiala8cbeed32015-09-08 22:22:33 +0000646 return (timed_out, passed, failed, unexpected_successes, pass_count,
647 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +0000648
Chaoren Linb6325d02015-08-12 18:02:54 +0000649
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000650def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +0000651 # returns a set of test filenames that might timeout
652 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +0000653 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000654 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +0000655 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000656 else:
657 m = re.search('remote-(\w+)', platform_name)
658 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +0000659
660 expected_timeout = set()
661
662 if target.startswith("linux"):
663 expected_timeout |= {
Vince Harronde92b522015-05-13 23:59:03 +0000664 "TestAttachDenied.py",
Chaoren Lin0b8bb3d2015-07-22 20:52:17 +0000665 "TestProcessAttach.py",
Vince Harron06381732015-05-12 23:10:36 +0000666 "TestConnectRemote.py",
667 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +0000668 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +0000669 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +0000670
671 # Times out in ~10% of the times on the build bot
672 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +0000673 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +0000674 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +0000675 "TestThreadStepOut.py",
Chaoren Linb0138022015-08-19 18:39:25 +0000676 "TestChangeProcessGroup.py",
Vince Harron06381732015-05-12 23:10:36 +0000677 }
678 elif target.startswith("android"):
679 expected_timeout |= {
680 "TestExitDuringStep.py",
681 "TestHelloWorld.py",
682 }
Chaoren Linfebef1b2015-08-19 17:22:12 +0000683 if host.startswith("win32"):
684 expected_timeout |= {
685 "TestEvents.py",
686 "TestThreadStates.py",
687 }
Ed Maste4dd8fba2015-05-14 16:25:52 +0000688 elif target.startswith("freebsd"):
689 expected_timeout |= {
690 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +0000691 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +0000692 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +0000693 "TestWatchpointConditionAPI.py",
694 }
Vince Harron0f173ac2015-05-18 19:36:33 +0000695 elif target.startswith("darwin"):
696 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +0000697 # times out on MBP Retina, Mid 2012
698 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +0000699 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +0000700 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +0000701 }
Vince Harron06381732015-05-12 23:10:36 +0000702 return expected_timeout
703
Chaoren Linb6325d02015-08-12 18:02:54 +0000704
Pavel Labathfad30cf2015-06-29 14:16:51 +0000705def getDefaultTimeout(platform_name):
706 if os.getenv("LLDB_TEST_TIMEOUT"):
707 return os.getenv("LLDB_TEST_TIMEOUT")
708
709 if platform_name is None:
710 platform_name = sys.platform
711
712 if platform_name.startswith("remote-"):
713 return "10m"
714 else:
715 return "4m"
716
Chaoren Linb6325d02015-08-12 18:02:54 +0000717
Vince Harron0b9dbb52015-05-21 18:18:52 +0000718def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +0000719 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +0000720 os.utime(fname, times)
721
Chaoren Linb6325d02015-08-12 18:02:54 +0000722
Vince Harrondcc2b9f2015-05-27 04:40:36 +0000723def find(pattern, path):
724 result = []
725 for root, dirs, files in os.walk(path):
726 for name in files:
727 if fnmatch.fnmatch(name, pattern):
728 result.append(os.path.join(root, name))
729 return result
730
Chaoren Linb6325d02015-08-12 18:02:54 +0000731
Todd Fiala8cbeed32015-09-08 22:22:33 +0000732def get_test_runner_strategies(num_threads):
733 """Returns the test runner strategies by name in a dictionary.
734
735 @param num_threads specifies the number of threads/processes
736 that will be used for concurrent test runners.
737
738 @return dictionary with key as test runner strategy name and
739 value set to a callable object that takes the test work item
740 and returns a test result tuple.
741 """
742 return {
743 # multiprocessing supports ctrl-c and does not use
744 # multiprocessing.Pool.
745 "multiprocessing":
746 (lambda work_items: multiprocessing_test_runner(
747 num_threads, work_items)),
748
749 # multiprocessing-pool uses multiprocessing.Pool but
750 # does not support Ctrl-C.
751 "multiprocessing-pool":
752 (lambda work_items: multiprocessing_test_runner_pool(
753 num_threads, work_items)),
754
755 # threading uses a hand-rolled worker pool much
756 # like multiprocessing, but instead uses in-process
757 # worker threads. This one supports Ctrl-C.
758 "threading":
759 (lambda work_items: threading_test_runner(num_threads, work_items)),
760
761 # threading-pool uses threading for the workers (in-process)
762 # and uses the multiprocessing.pool thread-enabled pool.
763 "threading-pool":
764 (lambda work_items: threading_test_runner_pool(
765 num_threads, work_items)),
766
767 # serial uses the subprocess-based, single process
768 # test runner. This provides process isolation but
769 # no concurrent test running.
770 "serial":
771 inprocess_exec_test_runner
772 }
773
774
775def main(print_details_on_success, num_threads, test_subdir,
776 test_runner_name):
Todd Fialafed95662015-09-03 18:58:44 +0000777 """Run dotest.py in inferior mode in parallel.
778
779 @param print_details_on_success the parsed value of the output-on-success
780 command line argument. When True, details of a successful dotest inferior
781 are printed even when everything succeeds. The normal behavior is to
782 not print any details when all the inferior tests pass.
783
784 @param num_threads the parsed value of the num-threads command line
785 argument.
786
787 @param test_subdir optionally specifies a subdir to limit testing
788 within. May be None if the entire test tree is to be used. This subdir
789 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000790
791 @param test_runner_name if specified, contains the test runner
792 name which selects the strategy used to run the isolated and
793 optionally concurrent test runner. Specify None to allow the
794 system to choose the most appropriate test runner given desired
795 thread count and OS type.
796
Todd Fialafed95662015-09-03 18:58:44 +0000797 """
798
799 dotest_argv = sys.argv[1:]
800
801 global output_on_success
802 output_on_success = print_details_on_success
803
Vince Harrond5fa1022015-05-10 15:24:12 +0000804 # We can't use sys.path[0] to determine the script directory
805 # because it doesn't work under a debugger
Vince Harrone06a7a82015-05-12 23:12:19 +0000806 test_directory = os.path.dirname(os.path.realpath(__file__))
Johnny Chene8d9dc62011-10-31 19:04:07 +0000807 parser = OptionParser(usage="""\
808Run lldb test suite using a separate process for each test file.
Vince Harronede59652015-01-08 02:11:26 +0000809
Siva Chandra2d7832e2015-05-08 23:08:53 +0000810 Each test will run with a time limit of 10 minutes by default.
Vince Harronede59652015-01-08 02:11:26 +0000811
Siva Chandra2d7832e2015-05-08 23:08:53 +0000812 Override the default time limit of 10 minutes by setting
Vince Harronede59652015-01-08 02:11:26 +0000813 the environment variable LLDB_TEST_TIMEOUT.
814
815 E.g., export LLDB_TEST_TIMEOUT=10m
816
817 Override the time limit for individual tests by setting
818 the environment variable LLDB_[TEST NAME]_TIMEOUT.
819
820 E.g., export LLDB_TESTCONCURRENTEVENTS_TIMEOUT=2m
821
822 Set to "0" to run without time limit.
823
824 E.g., export LLDB_TEST_TIMEOUT=0
825 or export LLDB_TESTCONCURRENTEVENTS_TIMEOUT=0
Johnny Chene8d9dc62011-10-31 19:04:07 +0000826""")
Vince Harron8994fed2015-05-22 19:49:23 +0000827 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +0000828 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +0000829 dotest_options = dotest_args.parse_args(parser, dotest_argv)
830
Vince Harron41657cc2015-05-21 18:15:09 +0000831 if not dotest_options.s:
832 # no session log directory, we need to add this to prevent
833 # every dotest invocation from creating its own directory
834 import datetime
835 # The windows platforms don't like ':' in the pathname.
Chaoren Linb6325d02015-08-12 18:02:54 +0000836 timestamp_started = datetime.datetime.now().strftime("%F-%H_%M_%S")
Vince Harron41657cc2015-05-21 18:15:09 +0000837 dotest_argv.append('-s')
838 dotest_argv.append(timestamp_started)
Vince Harron0b9dbb52015-05-21 18:18:52 +0000839 dotest_options.s = timestamp_started
840
841 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +0000842
Vince Harrone06a7a82015-05-12 23:12:19 +0000843 # The root directory was specified on the command line
Todd Fialafed95662015-09-03 18:58:44 +0000844 if test_subdir and len(test_subdir) > 0:
845 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +0000846 else:
Todd Fialafed95662015-09-03 18:58:44 +0000847 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +0000848
Vince Harrondcc2b9f2015-05-27 04:40:36 +0000849 # clean core files in test tree from previous runs (Linux)
850 cores = find('core.*', test_subdir)
851 for core in cores:
852 os.unlink(core)
853
Todd Fialafed95662015-09-03 18:58:44 +0000854 if not num_threads:
Greg Clayton2256d0d2014-03-24 23:01:57 +0000855 num_threads_str = os.environ.get("LLDB_TEST_THREADS")
856 if num_threads_str:
857 num_threads = int(num_threads_str)
Greg Clayton2256d0d2014-03-24 23:01:57 +0000858 else:
Ed Mastecec2a5b2014-11-21 02:41:25 +0000859 num_threads = multiprocessing.cpu_count()
860 if num_threads < 1:
861 num_threads = 1
Johnny Chene8d9dc62011-10-31 19:04:07 +0000862
Daniel Maleab42556f2013-04-19 18:32:53 +0000863 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +0000864
865 # Figure out which testrunner strategy we'll use.
866 runner_strategies_by_name = get_test_runner_strategies(num_threads)
867
868 # If the user didn't specify a test runner strategy, determine
869 # the default now based on number of threads and OS type.
870 if not test_runner_name:
871 if num_threads == 1:
872 # Use the serial runner.
873 test_runner_name = "serial"
874 elif os.name == "nt":
875 # Currently the multiprocessing test runner with ctrl-c
876 # support isn't running correctly on nt. Use the pool
877 # support without ctrl-c.
878 test_runner_name = "multiprocessing-pool"
879 else:
880 # For everyone else, use the ctrl-c-enabled
881 # multiprocessing support.
882 test_runner_name = "multiprocessing"
883
884 if test_runner_name not in runner_strategies_by_name:
885 raise Exception("specified testrunner name '{}' unknown. "
886 "Valid choices: {}".format(
887 test_runner_name,
888 runner_strategies_by_name.keys()))
889 test_runner_func = runner_strategies_by_name[test_runner_name]
890
891 summary_results = walk_and_invoke(
892 test_directory, test_subdir, dotest_argv, test_runner_func)
893
894 (timed_out, passed, failed, unexpected_successes, pass_count,
895 fail_count) = summary_results
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000896
Vince Harron17f429f2014-12-13 00:08:19 +0000897 timed_out = set(timed_out)
Chaoren Line80372a2015-08-12 18:02:53 +0000898 num_test_files = len(passed) + len(failed)
899 num_test_cases = pass_count + fail_count
Daniel Maleab42556f2013-04-19 18:32:53 +0000900
Vince Harrondcc2b9f2015-05-27 04:40:36 +0000901 # move core files into session dir
902 cores = find('core.*', test_subdir)
903 for core in cores:
904 dst = core.replace(test_directory, "")[1:]
905 dst = dst.replace(os.path.sep, "-")
906 os.rename(core, os.path.join(session_dir, dst))
907
Vince Harron06381732015-05-12 23:10:36 +0000908 # remove expected timeouts from failures
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000909 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
Vince Harron06381732015-05-12 23:10:36 +0000910 for xtime in expected_timeout:
911 if xtime in timed_out:
912 timed_out.remove(xtime)
913 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +0000914 result = "ExpectedTimeout"
915 elif xtime in passed:
916 result = "UnexpectedCompletion"
917 else:
918 result = None # failed
919
920 if result:
921 test_name = os.path.splitext(xtime)[0]
922 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +0000923
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000924 print
Chaoren Lin5a59e462015-08-12 18:02:51 +0000925 sys.stdout.write("Ran %d test suites" % num_test_files)
926 if num_test_files > 0:
927 sys.stdout.write(" (%d failed) (%f%%)" % (
928 len(failed), 100.0 * len(failed) / num_test_files))
929 print
Chaoren Line80372a2015-08-12 18:02:53 +0000930 sys.stdout.write("Ran %d test cases" % num_test_cases)
931 if num_test_cases > 0:
Chaoren Lin5a59e462015-08-12 18:02:51 +0000932 sys.stdout.write(" (%d failed) (%f%%)" % (
Chaoren Line80372a2015-08-12 18:02:53 +0000933 fail_count, 100.0 * fail_count / num_test_cases))
Chaoren Lin5a59e462015-08-12 18:02:51 +0000934 print
Zachary Turner4cceca72015-08-14 16:45:32 +0000935 exit_code = 0
936
Daniel Maleacbaef262013-02-15 21:31:37 +0000937 if len(failed) > 0:
Shawn Best13491c42014-10-22 19:29:00 +0000938 failed.sort()
Ying Chen10ed1a92015-05-28 23:51:49 +0000939 print "Failing Tests (%d)" % len(failed)
Daniel Maleacbaef262013-02-15 21:31:37 +0000940 for f in failed:
Vince Harron17f429f2014-12-13 00:08:19 +0000941 print "%s: LLDB (suite) :: %s (%s)" % (
942 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
943 )
Zachary Turner4cceca72015-08-14 16:45:32 +0000944 exit_code = 1
945
946 if len(unexpected_successes) > 0:
947 unexpected_successes.sort()
948 print "\nUnexpected Successes (%d)" % len(unexpected_successes)
949 for u in unexpected_successes:
950 print "UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info)
951
952 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +0000953
954if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +0000955 sys.stderr.write(
956 "error: dosep.py no longer supports being called directly. "
957 "Please call dotest.py directly. The dosep.py-specific arguments "
958 "have been added under the Parallel processing arguments.\n")
959 sys.exit(128)