blob: 90d57123ae8a00e13906bf81572f3ca0568eb517 [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 Fiala68615ce2015-09-15 21:38:04 +000035import asyncore
Vince Harrondcc2b9f2015-05-27 04:40:36 +000036import fnmatch
Todd Fiala8cbeed32015-09-08 22:22:33 +000037import multiprocessing
38import multiprocessing.pool
39import os
Todd Fiala3f0a3602014-07-08 06:42:37 +000040import platform
Todd Fiala8cbeed32015-09-08 22:22:33 +000041import Queue
Vince Harron06381732015-05-12 23:10:36 +000042import re
Todd Fiala8cbeed32015-09-08 22:22:33 +000043import signal
Vince Harron17f429f2014-12-13 00:08:19 +000044import subprocess
Todd Fiala3f0a3602014-07-08 06:42:37 +000045import sys
Todd Fiala8cbeed32015-09-08 22:22:33 +000046import threading
47
Todd Fiala68615ce2015-09-15 21:38:04 +000048import dotest_channels
Todd Fiala8cbeed32015-09-08 22:22:33 +000049import dotest_args
Steve Puccibefe2b12014-03-07 00:01:11 +000050
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
Todd Fiala68615ce2015-09-15 21:38:04 +000079RESULTS_FORMATTER = None
80RUNNER_PROCESS_ASYNC_MAP = None
81RESULTS_LISTENER_CHANNEL = None
Chaoren Linb6325d02015-08-12 18:02:54 +000082
Todd Fiala33896a92015-09-18 21:01:13 +000083"""Contains an optional function pointer that can return the worker index
84 for the given thread/process calling it. Returns a 0-based index."""
85GET_WORKER_INDEX = None
86
87def setup_global_variables(
88 lock, counter, total, name_len, options, worker_index_map):
Chaoren Linffc63b02015-08-12 18:02:49 +000089 global output_lock, test_counter, total_tests, test_name_len
90 global dotest_options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000091 output_lock = lock
92 test_counter = counter
93 total_tests = total
Chaoren Linffc63b02015-08-12 18:02:49 +000094 test_name_len = name_len
Pavel Labath05ab2372015-07-06 15:57:52 +000095 dotest_options = options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000096
Todd Fiala33896a92015-09-18 21:01:13 +000097 if worker_index_map is not None:
98 # We'll use the output lock for this to avoid sharing another lock.
99 # This won't be used much.
100 index_lock = lock
101
102 def get_worker_index_use_pid():
103 """Returns a 0-based, process-unique index for the worker."""
104 pid = os.getpid()
105 with index_lock:
106 if pid not in worker_index_map:
107 worker_index_map[pid] = len(worker_index_map)
108 return worker_index_map[pid]
109
110 global GET_WORKER_INDEX
111 GET_WORKER_INDEX = get_worker_index_use_pid
112
Chaoren Linb6325d02015-08-12 18:02:54 +0000113
Zachary Turner38e64172015-08-10 17:46:11 +0000114def report_test_failure(name, command, output):
115 global output_lock
116 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000117 print >> sys.stderr
Zachary Turner38e64172015-08-10 17:46:11 +0000118 print >> sys.stderr, output
Chaoren Linffc63b02015-08-12 18:02:49 +0000119 print >> sys.stderr, "[%s FAILED]" % name
Zachary Turner38e64172015-08-10 17:46:11 +0000120 print >> sys.stderr, "Command invoked: %s" % ' '.join(command)
Chaoren Linffc63b02015-08-12 18:02:49 +0000121 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000122
Chaoren Linb6325d02015-08-12 18:02:54 +0000123
Zachary Turner38e64172015-08-10 17:46:11 +0000124def report_test_pass(name, output):
125 global output_lock, output_on_success
126 with output_lock:
127 if output_on_success:
Chaoren Linffc63b02015-08-12 18:02:49 +0000128 print >> sys.stderr
Zachary Turner38e64172015-08-10 17:46:11 +0000129 print >> sys.stderr, output
Chaoren Linffc63b02015-08-12 18:02:49 +0000130 print >> sys.stderr, "[%s PASSED]" % name
131 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000132
Chaoren Linb6325d02015-08-12 18:02:54 +0000133
Chaoren Linffc63b02015-08-12 18:02:49 +0000134def update_progress(test_name=""):
135 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000136 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000137 counter_len = len(str(total_tests))
138 sys.stderr.write(
139 "\r%*d out of %d test suites processed - %-*s" %
140 (counter_len, test_counter.value, total_tests,
141 test_name_len.value, test_name))
142 if len(test_name) > test_name_len.value:
143 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000144 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000145 sys.stdout.flush()
146 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000147
Chaoren Linb6325d02015-08-12 18:02:54 +0000148
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000149def parse_test_results(output):
150 passes = 0
151 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000152 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000153 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000154 pass_count = re.search("^RESULT:.*([0-9]+) passes",
155 result, re.MULTILINE)
156 fail_count = re.search("^RESULT:.*([0-9]+) failures",
157 result, re.MULTILINE)
158 error_count = re.search("^RESULT:.*([0-9]+) errors",
159 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000160 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
161 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000162 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000163 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000164 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000165 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000166 if unexpected_success_count is not None:
167 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000168 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000169 failures = failures + int(error_count.group(1))
170 pass
Zachary Turner4cceca72015-08-14 16:45:32 +0000171 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000172
Chaoren Linb6325d02015-08-12 18:02:54 +0000173
Todd Fiala8cbeed32015-09-08 22:22:33 +0000174def call_with_timeout(command, timeout, name, inferior_pid_events):
Todd Fiala68615ce2015-09-15 21:38:04 +0000175 """Run command with a timeout if possible.
176 -s QUIT will create a coredump if they are enabled on your system
177 """
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000178 process = None
179 if timeout_command and timeout != "0":
180 command = [timeout_command, '-s', 'QUIT', timeout] + command
Todd Fiala68615ce2015-09-15 21:38:04 +0000181
Todd Fiala33896a92015-09-18 21:01:13 +0000182 if GET_WORKER_INDEX is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000183 try:
184 worker_index = GET_WORKER_INDEX()
185 command.extend([
186 "--event-add-entries", "worker_index={}".format(worker_index)])
187 except:
188 # Ctrl-C does bad things to multiprocessing.Manager.dict() lookup.
189 pass
190
Chaoren Linb6325d02015-08-12 18:02:54 +0000191 # Specifying a value for close_fds is unsupported on Windows when using
192 # subprocess.PIPE
Zachary Turnerdc494d52015-02-07 00:14:55 +0000193 if os.name != "nt":
Chaoren Linb6325d02015-08-12 18:02:54 +0000194 process = subprocess.Popen(command,
195 stdin=subprocess.PIPE,
196 stdout=subprocess.PIPE,
197 stderr=subprocess.PIPE,
198 close_fds=True)
Zachary Turnerdc494d52015-02-07 00:14:55 +0000199 else:
Chaoren Linb6325d02015-08-12 18:02:54 +0000200 process = subprocess.Popen(command,
201 stdin=subprocess.PIPE,
202 stdout=subprocess.PIPE,
203 stderr=subprocess.PIPE)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000204 inferior_pid = process.pid
205 if inferior_pid_events:
206 inferior_pid_events.put_nowait(('created', inferior_pid))
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000207 output = process.communicate()
Todd Fiala68615ce2015-09-15 21:38:04 +0000208
209 # The inferior should now be entirely wrapped up.
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000210 exit_status = process.returncode
Todd Fiala68615ce2015-09-15 21:38:04 +0000211 if exit_status is None:
212 raise Exception(
213 "no exit status available after the inferior dotest.py "
214 "should have completed")
215
Todd Fiala8cbeed32015-09-08 22:22:33 +0000216 if inferior_pid_events:
217 inferior_pid_events.put_nowait(('destroyed', inferior_pid))
218
Zachary Turner4cceca72015-08-14 16:45:32 +0000219 passes, failures, unexpected_successes = parse_test_results(output)
Zachary Turner38e64172015-08-10 17:46:11 +0000220 if exit_status == 0:
Chaoren Linb6325d02015-08-12 18:02:54 +0000221 # stdout does not have any useful information from 'dotest.py',
222 # only stderr does.
Zachary Turner38e64172015-08-10 17:46:11 +0000223 report_test_pass(name, output[1])
224 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000225 # TODO need to differentiate a failing test from a run that
226 # was broken out of by a SIGTERM/SIGKILL, reporting those as
227 # an error. If a signal-based completion, need to call that
228 # an error.
Zachary Turner38e64172015-08-10 17:46:11 +0000229 report_test_failure(name, command, output[1])
Zachary Turner4cceca72015-08-14 16:45:32 +0000230 return name, exit_status, passes, failures, unexpected_successes
Johnny Chene8d9dc62011-10-31 19:04:07 +0000231
Chaoren Linb6325d02015-08-12 18:02:54 +0000232
Todd Fiala8cbeed32015-09-08 22:22:33 +0000233def process_dir(root, files, test_root, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000234 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000235 results = []
Steve Puccibefe2b12014-03-07 00:01:11 +0000236 for name in files:
Zachary Turnerf6896b02015-01-05 19:37:03 +0000237 script_file = os.path.join(test_root, "dotest.py")
Zachary Turnerf6896b02015-01-05 19:37:03 +0000238 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000239 dotest_argv +
Todd Fialafed95662015-09-03 18:58:44 +0000240 ["--inferior", "-p", name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000241
242 timeout_name = os.path.basename(os.path.splitext(name)[0]).upper()
243
Chaoren Linb6325d02015-08-12 18:02:54 +0000244 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
245 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000246
Todd Fiala8cbeed32015-09-08 22:22:33 +0000247 results.append(call_with_timeout(
248 command, timeout, name, inferior_pid_events))
Vince Harron17f429f2014-12-13 00:08:19 +0000249
Zachary Turner4cceca72015-08-14 16:45:32 +0000250 # result = (name, status, passes, failures, unexpected_successes)
251 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000252 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000253 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000254 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000255 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000256 if status != ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000257 unexpected_passes = [name for name, _, _, _, unexpected_successes in results
258 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000259
Chaoren Line80372a2015-08-12 18:02:53 +0000260 pass_count = sum([result[2] for result in results])
261 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000262
Zachary Turner4cceca72015-08-14 16:45:32 +0000263 return (timed_out, passed, failed, unexpected_passes, pass_count, fail_count)
Steve Puccibefe2b12014-03-07 00:01:11 +0000264
265in_q = None
266out_q = None
267
Chaoren Linb6325d02015-08-12 18:02:54 +0000268
Todd Fiala8cbeed32015-09-08 22:22:33 +0000269def process_dir_worker_multiprocessing(
270 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000271 a_dotest_options, job_queue, result_queue, inferior_pid_events,
272 worker_index_map):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000273 """Worker thread main loop when in multiprocessing mode.
Steve Puccibefe2b12014-03-07 00:01:11 +0000274 Takes one directory specification at a time and works on it."""
Todd Fiala8cbeed32015-09-08 22:22:33 +0000275
276 # Shut off interrupt handling in the child process.
277 signal.signal(signal.SIGINT, signal.SIG_IGN)
278
279 # Setup the global state for the worker process.
280 setup_global_variables(
281 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000282 a_dotest_options, worker_index_map)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000283
284 # Keep grabbing entries from the queue until done.
285 while not job_queue.empty():
286 try:
287 job = job_queue.get(block=False)
288 result = process_dir(job[0], job[1], job[2], job[3],
289 inferior_pid_events)
290 result_queue.put(result)
291 except Queue.Empty:
292 # Fine, we're done.
293 pass
Steve Puccibefe2b12014-03-07 00:01:11 +0000294
Chaoren Linb6325d02015-08-12 18:02:54 +0000295
Todd Fiala8cbeed32015-09-08 22:22:33 +0000296def process_dir_worker_multiprocessing_pool(args):
297 return process_dir(*args)
298
299
Todd Fiala68615ce2015-09-15 21:38:04 +0000300def process_dir_worker_threading(job_queue, result_queue, inferior_pid_events):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000301 """Worker thread main loop when in threading mode.
302
303 This one supports the hand-rolled pooling support.
304
305 Takes one directory specification at a time and works on it."""
306
307 # Keep grabbing entries from the queue until done.
308 while not job_queue.empty():
309 try:
310 job = job_queue.get(block=False)
311 result = process_dir(job[0], job[1], job[2], job[3],
312 inferior_pid_events)
313 result_queue.put(result)
314 except Queue.Empty:
315 # Fine, we're done.
316 pass
317
318
319def process_dir_worker_threading_pool(args):
320 return process_dir(*args)
321
322
323def process_dir_mapper_inprocess(args):
324 """Map adapter for running the subprocess-based, non-threaded test runner.
325
326 @param args the process work item tuple
327 @return the test result tuple
328 """
329 return process_dir(*args)
330
331
332def collect_active_pids_from_pid_events(event_queue):
333 """
334 Returns the set of what should be active inferior pids based on
335 the event stream.
336
337 @param event_queue a multiprocessing.Queue containing events of the
338 form:
339 ('created', pid)
340 ('destroyed', pid)
341
342 @return set of inferior dotest.py pids activated but never completed.
343 """
344 active_pid_set = set()
345 while not event_queue.empty():
346 pid_event = event_queue.get_nowait()
347 if pid_event[0] == 'created':
348 active_pid_set.add(pid_event[1])
349 elif pid_event[0] == 'destroyed':
350 active_pid_set.remove(pid_event[1])
351 return active_pid_set
352
353
354def kill_all_worker_processes(workers, inferior_pid_events):
355 """
356 Kills all specified worker processes and their process tree.
357
358 @param workers a list of multiprocess.Process worker objects.
359 @param inferior_pid_events a multiprocess.Queue that contains
360 all inferior create and destroy events. Used to construct
361 the list of child pids still outstanding that need to be killed.
362 """
363 for worker in workers:
364 worker.terminate()
365 worker.join()
366
367 # Add all the child test pids created.
368 active_pid_set = collect_active_pids_from_pid_events(
369 inferior_pid_events)
370 for inferior_pid in active_pid_set:
371 print "killing inferior pid {}".format(inferior_pid)
372 os.kill(inferior_pid, signal.SIGKILL)
373
374
375def kill_all_worker_threads(workers, inferior_pid_events):
376 """
377 Kills all specified worker threads and their process tree.
378
379 @param workers a list of multiprocess.Process worker objects.
380 @param inferior_pid_events a multiprocess.Queue that contains
381 all inferior create and destroy events. Used to construct
382 the list of child pids still outstanding that need to be killed.
383 """
384
385 # Add all the child test pids created.
386 active_pid_set = collect_active_pids_from_pid_events(
387 inferior_pid_events)
388 for inferior_pid in active_pid_set:
389 print "killing inferior pid {}".format(inferior_pid)
390 os.kill(inferior_pid, signal.SIGKILL)
391
392 # We don't have a way to nuke the threads. However, since we killed
393 # all the inferiors, and we drained the job queue, this will be
394 # good enough. Wait cleanly for each worker thread to wrap up.
395 for worker in workers:
396 worker.join()
397
398
399def find_test_files_in_dir_tree(dir_root, found_func):
400 """Calls found_func for all the test files in the given dir hierarchy.
401
402 @param dir_root the path to the directory to start scanning
403 for test files. All files in this directory and all its children
404 directory trees will be searched.
405
406 @param found_func a callable object that will be passed
407 the parent directory (relative to dir_root) and the list of
408 test files from within that directory.
409 """
410 for root, _, files in os.walk(dir_root, topdown=False):
411 def is_test_filename(test_dir, base_filename):
412 """Returns True if the given filename matches the test name format.
413
414 @param test_dir the directory to check. Should be absolute or
415 relative to current working directory.
416
417 @param base_filename the base name of the filename to check for a
418 dherence to the python test case filename format.
419
420 @return True if name matches the python test case filename format.
421 """
422 # Not interested in symbolically linked files.
423 if os.path.islink(os.path.join(test_dir, base_filename)):
424 return False
425 # Only interested in test files with the "Test*.py" naming pattern.
426 return (base_filename.startswith("Test") and
427 base_filename.endswith(".py"))
428
429 tests = [filename for filename in files
430 if is_test_filename(root, filename)]
431 if tests:
432 found_func(root, tests)
433
434
435def initialize_global_vars_common(num_threads, test_work_items):
436 global total_tests, test_counter, test_name_len
437 total_tests = sum([len(item[1]) for item in test_work_items])
438 test_counter = multiprocessing.Value('i', 0)
439 test_name_len = multiprocessing.Value('i', 0)
440 print >> sys.stderr, "Testing: %d test suites, %d thread%s" % (
441 total_tests, num_threads, (num_threads > 1) * "s")
442 update_progress()
443
444
445def initialize_global_vars_multiprocessing(num_threads, test_work_items):
446 # Initialize the global state we'll use to communicate with the
447 # rest of the flat module.
448 global output_lock
449 output_lock = multiprocessing.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000450
Todd Fiala8cbeed32015-09-08 22:22:33 +0000451 initialize_global_vars_common(num_threads, test_work_items)
452
453
454def initialize_global_vars_threading(num_threads, test_work_items):
Todd Fiala33896a92015-09-18 21:01:13 +0000455 """Initializes global variables used in threading mode.
456 @param num_threads specifies the number of workers used.
457 @param test_work_items specifies all the work items
458 that will be processed.
459 """
Todd Fiala8cbeed32015-09-08 22:22:33 +0000460 # Initialize the global state we'll use to communicate with the
461 # rest of the flat module.
462 global output_lock
463 output_lock = threading.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000464
465 index_lock = threading.RLock()
466 index_map = {}
467
468 def get_worker_index_threading():
469 """Returns a 0-based, thread-unique index for the worker thread."""
470 thread_id = threading.current_thread().ident
471 with index_lock:
472 if thread_id not in index_map:
473 index_map[thread_id] = len(index_map)
474 return index_map[thread_id]
475
476
477 global GET_WORKER_INDEX
478 GET_WORKER_INDEX = get_worker_index_threading
479
Todd Fiala8cbeed32015-09-08 22:22:33 +0000480 initialize_global_vars_common(num_threads, test_work_items)
481
482
Todd Fiala68615ce2015-09-15 21:38:04 +0000483def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler):
484 """Provides a main loop that is Ctrl-C protected.
485
486 The main loop calls the main_op_func() repeatedly until done_func()
487 returns true. The ctrl_c_handler() method is called with a single
488 int parameter that contains the number of times the ctrl_c has been
489 hit (starting with 1). The ctrl_c_handler() should mutate whatever
490 it needs to have the done_func() return True as soon as it is desired
491 to exit the loop.
492 """
493 done = False
494 ctrl_c_count = 0
495
496 while not done:
497 try:
498 # See if we're done. Start with done check since it is
499 # the first thing executed after a Ctrl-C handler in the
500 # following loop.
501 done = done_func()
502 if not done:
503 # Run the main op once.
504 main_op_func()
505
506 except KeyboardInterrupt:
507 ctrl_c_count += 1
508 ctrl_c_handler(ctrl_c_count)
509
510
511def pump_workers_and_asyncore_map(workers, asyncore_map):
512 """Prunes out completed workers and maintains the asyncore loop.
513
514 The asyncore loop contains the optional socket listener
515 and handlers. When all workers are complete, this method
516 takes care of stopping the listener. It also runs the
517 asyncore loop for the given async map for 10 iterations.
518
519 @param workers the list of worker Thread/Process instances.
520
521 @param asyncore_map the asyncore threading-aware map that
522 indicates which channels are in use and still alive.
523 """
524
525 # Check on all the workers, removing them from the workers
526 # list as they complete.
527 dead_workers = []
528 for worker in workers:
529 # This non-blocking join call is what allows us
530 # to still receive keyboard interrupts.
531 worker.join(0.01)
532 if not worker.is_alive():
533 dead_workers.append(worker)
534 # Clear out the completed workers
535 for dead_worker in dead_workers:
536 workers.remove(dead_worker)
537
538 # If there are no more workers and there is a listener,
539 # close the listener.
540 global RESULTS_LISTENER_CHANNEL
541 if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None:
542 RESULTS_LISTENER_CHANNEL.close()
543 RESULTS_LISTENER_CHANNEL = None
544
545 # Pump the asyncore map if it isn't empty.
546 if len(asyncore_map) > 0:
547 asyncore.loop(0.1, False, asyncore_map, 10)
548
549
550def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events,
551 stop_all_inferiors_func):
552 """Performs the appropriate ctrl-c action for non-pool parallel test runners
553
554 @param ctrl_c_count starting with 1, indicates the number of times ctrl-c
555 has been intercepted. The value is 1 on the first intercept, 2 on the
556 second, etc.
557
558 @param job_queue a Queue object that contains the work still outstanding
559 (i.e. hasn't been assigned to a worker yet).
560
561 @param workers list of Thread or Process workers.
562
563 @param inferior_pid_events specifies a Queue of inferior process
564 construction and destruction events. Used to build the list of inferior
565 processes that should be killed if we get that far.
566
567 @param stop_all_inferiors_func a callable object that takes the
568 workers and inferior_pid_events parameters (in that order) if a hard
569 stop is to be used on the workers.
570 """
571
572 # Print out which Ctrl-C we're handling.
573 key_name = [
574 "first",
575 "second",
576 "third",
577 "many"]
578
579 if ctrl_c_count < len(key_name):
580 name_index = ctrl_c_count - 1
581 else:
582 name_index = len(key_name) - 1
583 message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index])
584 with output_lock:
585 print message
586
587 if ctrl_c_count == 1:
588 # Remove all outstanding items from the work queue so we stop
589 # doing any more new work.
590 while not job_queue.empty():
591 try:
592 # Just drain it to stop more work from being started.
593 job_queue.get_nowait()
594 except Queue.Empty:
595 pass
596 with output_lock:
597 print "Stopped more work from being started."
598 elif ctrl_c_count == 2:
599 # Try to stop all inferiors, even the ones currently doing work.
600 stop_all_inferiors_func(workers, inferior_pid_events)
601 else:
602 with output_lock:
603 print "All teardown activities kicked off, should finish soon."
604
605
606def workers_and_async_done(workers, async_map):
607 """Returns True if the workers list and asyncore channels are all done.
608
609 @param workers list of workers (threads/processes). These must adhere
610 to the threading Thread or multiprocessing.Process interface.
611
612 @param async_map the threading-aware asyncore channel map to check
613 for live channels.
614
615 @return False if the workers list exists and has any entries in it, or
616 if the async_map exists and has any entries left in it; otherwise, True.
617 """
618 if workers is not None and len(workers) > 0:
619 # We're not done if we still have workers left.
620 return False
621 if async_map is not None and len(async_map) > 0:
622 return False
623 # We're done.
624 return True
625
626
Todd Fiala8cbeed32015-09-08 22:22:33 +0000627def multiprocessing_test_runner(num_threads, test_work_items):
628 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
629
630 This concurrent test runner is based on the multiprocessing
631 library, and rolls its own worker pooling strategy so it
632 can handle Ctrl-C properly.
633
634 This test runner is known to have an issue running on
635 Windows platforms.
636
637 @param num_threads the number of worker processes to use.
638
639 @param test_work_items the iterable of test work item tuples
640 to run.
641 """
642
643 # Initialize our global state.
644 initialize_global_vars_multiprocessing(num_threads, test_work_items)
645
646 # Create jobs.
647 job_queue = multiprocessing.Queue(len(test_work_items))
648 for test_work_item in test_work_items:
649 job_queue.put(test_work_item)
650
651 result_queue = multiprocessing.Queue(len(test_work_items))
652
653 # Create queues for started child pids. Terminating
654 # the multiprocess processes does not terminate the
655 # child processes they spawn. We can remove this tracking
656 # if/when we move to having the multiprocess process directly
657 # perform the test logic. The Queue size needs to be able to
658 # hold 2 * (num inferior dotest.py processes started) entries.
659 inferior_pid_events = multiprocessing.Queue(4096)
660
Todd Fiala33896a92015-09-18 21:01:13 +0000661 # Worker dictionary allows each worker to figure out its worker index.
662 manager = multiprocessing.Manager()
663 worker_index_map = manager.dict()
664
Todd Fiala8cbeed32015-09-08 22:22:33 +0000665 # Create workers. We don't use multiprocessing.Pool due to
666 # challenges with handling ^C keyboard interrupts.
667 workers = []
668 for _ in range(num_threads):
669 worker = multiprocessing.Process(
670 target=process_dir_worker_multiprocessing,
671 args=(output_lock,
672 test_counter,
673 total_tests,
674 test_name_len,
675 dotest_options,
676 job_queue,
677 result_queue,
Todd Fiala33896a92015-09-18 21:01:13 +0000678 inferior_pid_events,
679 worker_index_map))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000680 worker.start()
681 workers.append(worker)
682
Todd Fiala68615ce2015-09-15 21:38:04 +0000683 # Main loop: wait for all workers to finish and wait for
684 # the socket handlers to wrap up.
685 ctrl_c_loop(
686 # Main operation of loop
687 lambda: pump_workers_and_asyncore_map(
688 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000689
Todd Fiala68615ce2015-09-15 21:38:04 +0000690 # Return True when we're done with the main loop.
691 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000692
Todd Fiala68615ce2015-09-15 21:38:04 +0000693 # Indicate what we do when we receive one or more Ctrl-Cs.
694 lambda ctrl_c_count: handle_ctrl_c(
695 ctrl_c_count, job_queue, workers, inferior_pid_events,
696 kill_all_worker_processes))
697
698 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000699 test_results = []
700 while not result_queue.empty():
701 test_results.append(result_queue.get(block=False))
702 return test_results
703
704
Todd Fiala68615ce2015-09-15 21:38:04 +0000705def map_async_run_loop(future, channel_map, listener_channel):
706 """Blocks until the Pool.map_async completes and the channel completes.
707
708 @param future an AsyncResult instance from a Pool.map_async() call.
709
710 @param channel_map the asyncore dispatch channel map that should be pumped.
711 Optional: may be None.
712
713 @param listener_channel the channel representing a listener that should be
714 closed once the map_async results are available.
715
716 @return the results from the async_result instance.
717 """
718 map_results = None
719
720 done = False
721 while not done:
722 # Check if we need to reap the map results.
723 if map_results is None:
724 if future.ready():
725 # Get the results.
726 map_results = future.get()
727
728 # Close the runner process listener channel if we have
729 # one: no more connections will be incoming.
730 if listener_channel is not None:
731 listener_channel.close()
732
733 # Pump the asyncore loop if we have a listener socket.
734 if channel_map is not None:
735 asyncore.loop(0.01, False, channel_map, 10)
736
737 # Figure out if we're done running.
738 done = map_results is not None
739 if channel_map is not None:
740 # We have a runner process async map. Check if it
741 # is complete.
742 if len(channel_map) > 0:
743 # We still have an asyncore channel running. Not done yet.
744 done = False
745
746 return map_results
747
748
Todd Fiala8cbeed32015-09-08 22:22:33 +0000749def multiprocessing_test_runner_pool(num_threads, test_work_items):
750 # Initialize our global state.
751 initialize_global_vars_multiprocessing(num_threads, test_work_items)
752
Todd Fiala33896a92015-09-18 21:01:13 +0000753 manager = multiprocessing.Manager()
754 worker_index_map = manager.dict()
755
Todd Fiala8cbeed32015-09-08 22:22:33 +0000756 pool = multiprocessing.Pool(
757 num_threads,
758 initializer=setup_global_variables,
759 initargs=(output_lock, test_counter, total_tests, test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000760 dotest_options, worker_index_map))
Todd Fiala68615ce2015-09-15 21:38:04 +0000761
762 # Start the map operation (async mode).
763 map_future = pool.map_async(
764 process_dir_worker_multiprocessing_pool, test_work_items)
765 return map_async_run_loop(
766 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000767
768
769def threading_test_runner(num_threads, test_work_items):
770 """Provides hand-wrapped pooling threading-based test runner adapter
771 with Ctrl-C support.
772
773 This concurrent test runner is based on the threading
774 library, and rolls its own worker pooling strategy so it
775 can handle Ctrl-C properly.
776
777 @param num_threads the number of worker processes to use.
778
779 @param test_work_items the iterable of test work item tuples
780 to run.
781 """
782
783 # Initialize our global state.
784 initialize_global_vars_threading(num_threads, test_work_items)
785
786 # Create jobs.
787 job_queue = Queue.Queue()
788 for test_work_item in test_work_items:
789 job_queue.put(test_work_item)
790
791 result_queue = Queue.Queue()
792
793 # Create queues for started child pids. Terminating
794 # the threading threads does not terminate the
795 # child processes they spawn.
796 inferior_pid_events = Queue.Queue()
797
798 # Create workers. We don't use multiprocessing.pool.ThreadedPool
799 # due to challenges with handling ^C keyboard interrupts.
800 workers = []
801 for _ in range(num_threads):
802 worker = threading.Thread(
803 target=process_dir_worker_threading,
Todd Fiala68615ce2015-09-15 21:38:04 +0000804 args=(job_queue,
Todd Fiala8cbeed32015-09-08 22:22:33 +0000805 result_queue,
806 inferior_pid_events))
807 worker.start()
808 workers.append(worker)
809
Todd Fiala68615ce2015-09-15 21:38:04 +0000810 # Main loop: wait for all workers to finish and wait for
811 # the socket handlers to wrap up.
812 ctrl_c_loop(
813 # Main operation of loop
814 lambda: pump_workers_and_asyncore_map(
815 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000816
Todd Fiala68615ce2015-09-15 21:38:04 +0000817 # Return True when we're done with the main loop.
818 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000819
Todd Fiala68615ce2015-09-15 21:38:04 +0000820 # Indicate what we do when we receive one or more Ctrl-Cs.
821 lambda ctrl_c_count: handle_ctrl_c(
822 ctrl_c_count, job_queue, workers, inferior_pid_events,
823 kill_all_worker_threads))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000824
Todd Fiala68615ce2015-09-15 21:38:04 +0000825 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000826 test_results = []
827 while not result_queue.empty():
828 test_results.append(result_queue.get(block=False))
829 return test_results
830
831
832def threading_test_runner_pool(num_threads, test_work_items):
833 # Initialize our global state.
834 initialize_global_vars_threading(num_threads, test_work_items)
835
Todd Fiala68615ce2015-09-15 21:38:04 +0000836 pool = multiprocessing.pool.ThreadPool(num_threads)
837 map_future = pool.map_async(
838 process_dir_worker_threading_pool, test_work_items)
839
840 return map_async_run_loop(
841 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
842
843
844def asyncore_run_loop(channel_map):
845 try:
846 asyncore.loop(None, False, channel_map)
847 except:
848 # Swallow it, we're seeing:
849 # error: (9, 'Bad file descriptor')
850 # when the listener channel is closed. Shouldn't be the case.
851 pass
Todd Fiala8cbeed32015-09-08 22:22:33 +0000852
853
854def inprocess_exec_test_runner(test_work_items):
855 # Initialize our global state.
856 initialize_global_vars_multiprocessing(1, test_work_items)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000857
Todd Fiala33896a92015-09-18 21:01:13 +0000858 # We're always worker index 0
859 global GET_WORKER_INDEX
860 GET_WORKER_INDEX = lambda: 0
861
Todd Fiala68615ce2015-09-15 21:38:04 +0000862 # Run the listener and related channel maps in a separate thread.
863 # global RUNNER_PROCESS_ASYNC_MAP
864 global RESULTS_LISTENER_CHANNEL
865 if RESULTS_LISTENER_CHANNEL is not None:
866 socket_thread = threading.Thread(
867 target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP))
868 socket_thread.start()
869
870 # Do the work.
871 test_results = map(process_dir_mapper_inprocess, test_work_items)
872
873 # If we have a listener channel, shut it down here.
874 if RESULTS_LISTENER_CHANNEL is not None:
875 # Close down the channel.
876 RESULTS_LISTENER_CHANNEL.close()
877 RESULTS_LISTENER_CHANNEL = None
878
879 # Wait for the listener and handlers to complete.
880 socket_thread.join()
881
882 return test_results
Todd Fiala8cbeed32015-09-08 22:22:33 +0000883
884def walk_and_invoke(test_directory, test_subdir, dotest_argv,
885 test_runner_func):
Steve Puccibefe2b12014-03-07 00:01:11 +0000886 """Look for matched files and invoke test driver on each one.
887 In single-threaded mode, each test driver is invoked directly.
888 In multi-threaded mode, submit each test driver to a worker
Vince Harrone06a7a82015-05-12 23:12:19 +0000889 queue, and then wait for all to complete.
890
891 test_directory - lldb/test/ directory
Chaoren Linb6325d02015-08-12 18:02:54 +0000892 test_subdir - lldb/test/ or a subfolder with the tests we're interested in
893 running
Vince Harrone06a7a82015-05-12 23:12:19 +0000894 """
Todd Fiala68615ce2015-09-15 21:38:04 +0000895 # The async_map is important to keep all thread-related asyncore
896 # channels distinct when we call asyncore.loop() later on.
897 global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP
898 RUNNER_PROCESS_ASYNC_MAP = {}
899
900 # If we're outputting side-channel test results, create the socket
901 # listener channel and tell the inferior to send results to the
902 # port on which we'll be listening.
903 if RESULTS_FORMATTER is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000904 forwarding_func = RESULTS_FORMATTER.handle_event
Todd Fiala68615ce2015-09-15 21:38:04 +0000905 RESULTS_LISTENER_CHANNEL = (
906 dotest_channels.UnpicklingForwardingListenerChannel(
907 RUNNER_PROCESS_ASYNC_MAP, "localhost", 0, forwarding_func))
908 dotest_argv.append("--results-port")
909 dotest_argv.append(str(RESULTS_LISTENER_CHANNEL.address[1]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000910
911 # Collect the test files that we'll run.
912 test_work_items = []
Todd Fiala8cbeed32015-09-08 22:22:33 +0000913 find_test_files_in_dir_tree(
914 test_subdir, lambda testdir, test_files: test_work_items.append([
915 test_subdir, test_files, test_directory, dotest_argv, None]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000916
Todd Fiala8cbeed32015-09-08 22:22:33 +0000917 # Convert test work items into test results using whatever
918 # was provided as the test run function.
919 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +0000920
Todd Fiala8cbeed32015-09-08 22:22:33 +0000921 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +0000922 timed_out = sum([result[0] for result in test_results], [])
923 passed = sum([result[1] for result in test_results], [])
924 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +0000925 unexpected_successes = sum([result[3] for result in test_results], [])
926 pass_count = sum([result[4] for result in test_results])
927 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +0000928
Todd Fiala8cbeed32015-09-08 22:22:33 +0000929 return (timed_out, passed, failed, unexpected_successes, pass_count,
930 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +0000931
Chaoren Linb6325d02015-08-12 18:02:54 +0000932
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000933def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +0000934 # returns a set of test filenames that might timeout
935 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +0000936 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000937 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +0000938 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000939 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000940 m = re.search(r'remote-(\w+)', platform_name)
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000941 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +0000942
943 expected_timeout = set()
944
945 if target.startswith("linux"):
946 expected_timeout |= {
Chaoren Lin0b8bb3d2015-07-22 20:52:17 +0000947 "TestProcessAttach.py",
Vince Harron06381732015-05-12 23:10:36 +0000948 "TestConnectRemote.py",
949 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +0000950 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +0000951 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +0000952
953 # Times out in ~10% of the times on the build bot
954 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +0000955 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +0000956 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +0000957 "TestThreadStepOut.py",
958 }
959 elif target.startswith("android"):
960 expected_timeout |= {
961 "TestExitDuringStep.py",
962 "TestHelloWorld.py",
963 }
Chaoren Linfebef1b2015-08-19 17:22:12 +0000964 if host.startswith("win32"):
965 expected_timeout |= {
966 "TestEvents.py",
967 "TestThreadStates.py",
968 }
Ed Maste4dd8fba2015-05-14 16:25:52 +0000969 elif target.startswith("freebsd"):
970 expected_timeout |= {
971 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +0000972 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +0000973 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +0000974 "TestWatchpointConditionAPI.py",
975 }
Vince Harron0f173ac2015-05-18 19:36:33 +0000976 elif target.startswith("darwin"):
977 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +0000978 # times out on MBP Retina, Mid 2012
979 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +0000980 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +0000981 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +0000982 }
Vince Harron06381732015-05-12 23:10:36 +0000983 return expected_timeout
984
Chaoren Linb6325d02015-08-12 18:02:54 +0000985
Pavel Labathfad30cf2015-06-29 14:16:51 +0000986def getDefaultTimeout(platform_name):
987 if os.getenv("LLDB_TEST_TIMEOUT"):
988 return os.getenv("LLDB_TEST_TIMEOUT")
989
990 if platform_name is None:
991 platform_name = sys.platform
992
993 if platform_name.startswith("remote-"):
994 return "10m"
995 else:
996 return "4m"
997
Chaoren Linb6325d02015-08-12 18:02:54 +0000998
Vince Harron0b9dbb52015-05-21 18:18:52 +0000999def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +00001000 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +00001001 os.utime(fname, times)
1002
Chaoren Linb6325d02015-08-12 18:02:54 +00001003
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001004def find(pattern, path):
1005 result = []
1006 for root, dirs, files in os.walk(path):
1007 for name in files:
1008 if fnmatch.fnmatch(name, pattern):
1009 result.append(os.path.join(root, name))
1010 return result
1011
Chaoren Linb6325d02015-08-12 18:02:54 +00001012
Todd Fiala8cbeed32015-09-08 22:22:33 +00001013def get_test_runner_strategies(num_threads):
1014 """Returns the test runner strategies by name in a dictionary.
1015
1016 @param num_threads specifies the number of threads/processes
1017 that will be used for concurrent test runners.
1018
1019 @return dictionary with key as test runner strategy name and
1020 value set to a callable object that takes the test work item
1021 and returns a test result tuple.
1022 """
1023 return {
1024 # multiprocessing supports ctrl-c and does not use
1025 # multiprocessing.Pool.
1026 "multiprocessing":
1027 (lambda work_items: multiprocessing_test_runner(
1028 num_threads, work_items)),
1029
1030 # multiprocessing-pool uses multiprocessing.Pool but
1031 # does not support Ctrl-C.
1032 "multiprocessing-pool":
1033 (lambda work_items: multiprocessing_test_runner_pool(
1034 num_threads, work_items)),
1035
1036 # threading uses a hand-rolled worker pool much
1037 # like multiprocessing, but instead uses in-process
1038 # worker threads. This one supports Ctrl-C.
1039 "threading":
1040 (lambda work_items: threading_test_runner(num_threads, work_items)),
1041
1042 # threading-pool uses threading for the workers (in-process)
1043 # and uses the multiprocessing.pool thread-enabled pool.
Todd Fiala68615ce2015-09-15 21:38:04 +00001044 # This does not properly support Ctrl-C.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001045 "threading-pool":
1046 (lambda work_items: threading_test_runner_pool(
1047 num_threads, work_items)),
1048
1049 # serial uses the subprocess-based, single process
1050 # test runner. This provides process isolation but
Todd Fiala68615ce2015-09-15 21:38:04 +00001051 # no concurrent test execution.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001052 "serial":
1053 inprocess_exec_test_runner
1054 }
1055
1056
Todd Fiala68615ce2015-09-15 21:38:04 +00001057def _remove_option(args, option_name, removal_count):
1058 """Removes option and related option arguments from args array.
1059 @param args the array of command line arguments (in/out)
1060 @param option_name the full command line representation of the
1061 option that will be removed (including '--' or '-').
1062 @param the count of elements to remove. A value of 1 will remove
1063 just the found option, while 2 will remove the option and its first
1064 argument.
1065 """
1066 try:
1067 index = args.index(option_name)
1068 # Handle the exact match case.
1069 del args[index:index+removal_count]
1070 return
1071 except ValueError:
1072 # Thanks to argparse not handling options with known arguments
1073 # like other options parsing libraries (see
1074 # https://bugs.python.org/issue9334), we need to support the
1075 # --results-formatter-options={second-level-arguments} (note
1076 # the equal sign to fool the first-level arguments parser into
1077 # not treating the second-level arguments as first-level
1078 # options). We're certainly at risk of getting this wrong
1079 # since now we're forced into the business of trying to figure
1080 # out what is an argument (although I think this
1081 # implementation will suffice).
1082 regex_string = "^" + option_name + "="
1083 regex = re.compile(regex_string)
1084 for index in range(len(args)):
1085 match = regex.match(args[index])
1086 if match:
1087 print "found matching option= at index {}".format(index)
1088 del args[index]
1089 return
1090 print "failed to find regex '{}'".format(regex_string)
1091
1092 # We didn't find the option but we should have.
1093 raise Exception("failed to find option '{}' in args '{}'".format(
1094 option_name, args))
1095
1096
1097def adjust_inferior_options(dotest_argv):
1098 """Adjusts the commandline args array for inferiors.
1099
1100 This method adjusts the inferior dotest commandline options based
1101 on the parallel test runner's options. Some of the inferior options
1102 will need to change to properly handle aggregation functionality.
1103 """
1104 global dotest_options
1105
1106 # If we don't have a session directory, create one.
1107 if not dotest_options.s:
1108 # no session log directory, we need to add this to prevent
1109 # every dotest invocation from creating its own directory
1110 import datetime
1111 # The windows platforms don't like ':' in the pathname.
1112 timestamp_started = datetime.datetime.now().strftime("%F-%H_%M_%S")
1113 dotest_argv.append('-s')
1114 dotest_argv.append(timestamp_started)
1115 dotest_options.s = timestamp_started
1116
1117 # Adjust inferior results formatter options - if the parallel
1118 # test runner is collecting into the user-specified test results,
1119 # we'll have inferiors spawn with the --results-port option and
1120 # strip the original test runner options.
1121 if dotest_options.results_file is not None:
1122 _remove_option(dotest_argv, "--results-file", 2)
1123 if dotest_options.results_port is not None:
1124 _remove_option(dotest_argv, "--results-port", 2)
1125 if dotest_options.results_formatter is not None:
1126 _remove_option(dotest_argv, "--results-formatter", 2)
1127 if dotest_options.results_formatter_options is not None:
1128 _remove_option(dotest_argv, "--results-formatter-options", 2)
1129
Todd Fiala33896a92015-09-18 21:01:13 +00001130 # Remove test runner name if present.
1131 if dotest_options.test_runner_name is not None:
1132 _remove_option(dotest_argv, "--test-runner-name", 2)
1133
1134
Todd Fiala8cbeed32015-09-08 22:22:33 +00001135def main(print_details_on_success, num_threads, test_subdir,
Todd Fiala68615ce2015-09-15 21:38:04 +00001136 test_runner_name, results_formatter):
Todd Fialafed95662015-09-03 18:58:44 +00001137 """Run dotest.py in inferior mode in parallel.
1138
1139 @param print_details_on_success the parsed value of the output-on-success
1140 command line argument. When True, details of a successful dotest inferior
1141 are printed even when everything succeeds. The normal behavior is to
1142 not print any details when all the inferior tests pass.
1143
1144 @param num_threads the parsed value of the num-threads command line
1145 argument.
1146
1147 @param test_subdir optionally specifies a subdir to limit testing
1148 within. May be None if the entire test tree is to be used. This subdir
1149 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001150
1151 @param test_runner_name if specified, contains the test runner
1152 name which selects the strategy used to run the isolated and
1153 optionally concurrent test runner. Specify None to allow the
1154 system to choose the most appropriate test runner given desired
1155 thread count and OS type.
1156
Todd Fiala68615ce2015-09-15 21:38:04 +00001157 @param results_formatter if specified, provides the TestResultsFormatter
1158 instance that will format and output test result data from the
1159 side-channel test results. When specified, inferior dotest calls
1160 will send test results side-channel data over a socket to the parallel
1161 test runner, which will forward them on to results_formatter.
Todd Fialafed95662015-09-03 18:58:44 +00001162 """
1163
1164 dotest_argv = sys.argv[1:]
1165
Todd Fiala68615ce2015-09-15 21:38:04 +00001166 global output_on_success, RESULTS_FORMATTER
Todd Fialafed95662015-09-03 18:58:44 +00001167 output_on_success = print_details_on_success
Todd Fiala68615ce2015-09-15 21:38:04 +00001168 RESULTS_FORMATTER = results_formatter
Todd Fialafed95662015-09-03 18:58:44 +00001169
Vince Harrond5fa1022015-05-10 15:24:12 +00001170 # We can't use sys.path[0] to determine the script directory
1171 # because it doesn't work under a debugger
Vince Harron8994fed2015-05-22 19:49:23 +00001172 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +00001173 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +00001174 dotest_options = dotest_args.parse_args(parser, dotest_argv)
1175
Todd Fiala68615ce2015-09-15 21:38:04 +00001176 adjust_inferior_options(dotest_argv)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001177
1178 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +00001179
Vince Harrone06a7a82015-05-12 23:12:19 +00001180 # The root directory was specified on the command line
Todd Fiala68615ce2015-09-15 21:38:04 +00001181 test_directory = os.path.dirname(os.path.realpath(__file__))
Todd Fialafed95662015-09-03 18:58:44 +00001182 if test_subdir and len(test_subdir) > 0:
1183 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +00001184 else:
Todd Fialafed95662015-09-03 18:58:44 +00001185 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +00001186
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001187 # clean core files in test tree from previous runs (Linux)
1188 cores = find('core.*', test_subdir)
1189 for core in cores:
1190 os.unlink(core)
1191
Daniel Maleab42556f2013-04-19 18:32:53 +00001192 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +00001193
1194 # Figure out which testrunner strategy we'll use.
1195 runner_strategies_by_name = get_test_runner_strategies(num_threads)
1196
1197 # If the user didn't specify a test runner strategy, determine
1198 # the default now based on number of threads and OS type.
1199 if not test_runner_name:
1200 if num_threads == 1:
1201 # Use the serial runner.
1202 test_runner_name = "serial"
1203 elif os.name == "nt":
1204 # Currently the multiprocessing test runner with ctrl-c
1205 # support isn't running correctly on nt. Use the pool
1206 # support without ctrl-c.
1207 test_runner_name = "multiprocessing-pool"
1208 else:
1209 # For everyone else, use the ctrl-c-enabled
1210 # multiprocessing support.
1211 test_runner_name = "multiprocessing"
1212
1213 if test_runner_name not in runner_strategies_by_name:
1214 raise Exception("specified testrunner name '{}' unknown. "
1215 "Valid choices: {}".format(
1216 test_runner_name,
1217 runner_strategies_by_name.keys()))
1218 test_runner_func = runner_strategies_by_name[test_runner_name]
1219
1220 summary_results = walk_and_invoke(
1221 test_directory, test_subdir, dotest_argv, test_runner_func)
1222
1223 (timed_out, passed, failed, unexpected_successes, pass_count,
1224 fail_count) = summary_results
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +00001225
Vince Harron17f429f2014-12-13 00:08:19 +00001226 timed_out = set(timed_out)
Chaoren Line80372a2015-08-12 18:02:53 +00001227 num_test_files = len(passed) + len(failed)
1228 num_test_cases = pass_count + fail_count
Daniel Maleab42556f2013-04-19 18:32:53 +00001229
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001230 # move core files into session dir
1231 cores = find('core.*', test_subdir)
1232 for core in cores:
1233 dst = core.replace(test_directory, "")[1:]
1234 dst = dst.replace(os.path.sep, "-")
1235 os.rename(core, os.path.join(session_dir, dst))
1236
Vince Harron06381732015-05-12 23:10:36 +00001237 # remove expected timeouts from failures
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001238 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
Vince Harron06381732015-05-12 23:10:36 +00001239 for xtime in expected_timeout:
1240 if xtime in timed_out:
1241 timed_out.remove(xtime)
1242 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001243 result = "ExpectedTimeout"
1244 elif xtime in passed:
1245 result = "UnexpectedCompletion"
1246 else:
1247 result = None # failed
1248
1249 if result:
1250 test_name = os.path.splitext(xtime)[0]
1251 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +00001252
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +00001253 print
Chaoren Lin5a59e462015-08-12 18:02:51 +00001254 sys.stdout.write("Ran %d test suites" % num_test_files)
1255 if num_test_files > 0:
1256 sys.stdout.write(" (%d failed) (%f%%)" % (
1257 len(failed), 100.0 * len(failed) / num_test_files))
1258 print
Chaoren Line80372a2015-08-12 18:02:53 +00001259 sys.stdout.write("Ran %d test cases" % num_test_cases)
1260 if num_test_cases > 0:
Chaoren Lin5a59e462015-08-12 18:02:51 +00001261 sys.stdout.write(" (%d failed) (%f%%)" % (
Chaoren Line80372a2015-08-12 18:02:53 +00001262 fail_count, 100.0 * fail_count / num_test_cases))
Chaoren Lin5a59e462015-08-12 18:02:51 +00001263 print
Zachary Turner4cceca72015-08-14 16:45:32 +00001264 exit_code = 0
1265
Daniel Maleacbaef262013-02-15 21:31:37 +00001266 if len(failed) > 0:
Shawn Best13491c42014-10-22 19:29:00 +00001267 failed.sort()
Ying Chen10ed1a92015-05-28 23:51:49 +00001268 print "Failing Tests (%d)" % len(failed)
Daniel Maleacbaef262013-02-15 21:31:37 +00001269 for f in failed:
Vince Harron17f429f2014-12-13 00:08:19 +00001270 print "%s: LLDB (suite) :: %s (%s)" % (
1271 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
1272 )
Zachary Turner4cceca72015-08-14 16:45:32 +00001273 exit_code = 1
1274
1275 if len(unexpected_successes) > 0:
1276 unexpected_successes.sort()
1277 print "\nUnexpected Successes (%d)" % len(unexpected_successes)
1278 for u in unexpected_successes:
1279 print "UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info)
1280
1281 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001282
1283if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +00001284 sys.stderr.write(
1285 "error: dosep.py no longer supports being called directly. "
1286 "Please call dotest.py directly. The dosep.py-specific arguments "
1287 "have been added under the Parallel processing arguments.\n")
1288 sys.exit(128)