blob: 094282afcf6c1280cf3713f36e7cbba4c2864959 [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
Greg Clayton1827fc22015-09-19 00:39:09 +000047import test_results
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
Todd Fiala68615ce2015-09-15 21:38:04 +000078RESULTS_FORMATTER = None
79RUNNER_PROCESS_ASYNC_MAP = None
80RESULTS_LISTENER_CHANNEL = None
Chaoren Linb6325d02015-08-12 18:02:54 +000081
Todd Fiala33896a92015-09-18 21:01:13 +000082"""Contains an optional function pointer that can return the worker index
83 for the given thread/process calling it. Returns a 0-based index."""
84GET_WORKER_INDEX = None
85
Todd Fiala1cc97b42015-09-21 05:42:26 +000086
Todd Fiala33896a92015-09-18 21:01:13 +000087def 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:
Greg Clayton1827fc22015-09-19 00:39:09 +0000117 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
118 print >> sys.stderr
119 print >> sys.stderr, output
120 print >> sys.stderr, "[%s FAILED]" % name
121 print >> sys.stderr, "Command invoked: %s" % ' '.join(command)
Chaoren Linffc63b02015-08-12 18:02:49 +0000122 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000123
Chaoren Linb6325d02015-08-12 18:02:54 +0000124
Zachary Turner38e64172015-08-10 17:46:11 +0000125def report_test_pass(name, output):
126 global output_lock, output_on_success
127 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000128 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
129 if output_on_success:
130 print >> sys.stderr
131 print >> sys.stderr, output
132 print >> sys.stderr, "[%s PASSED]" % name
Chaoren Linffc63b02015-08-12 18:02:49 +0000133 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000134
Chaoren Linb6325d02015-08-12 18:02:54 +0000135
Chaoren Linffc63b02015-08-12 18:02:49 +0000136def update_progress(test_name=""):
137 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000138 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000139 counter_len = len(str(total_tests))
Greg Clayton1827fc22015-09-19 00:39:09 +0000140 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
141 sys.stderr.write(
142 "\r%*d out of %d test suites processed - %-*s" %
143 (counter_len, test_counter.value, total_tests,
144 test_name_len.value, test_name))
Chaoren Linffc63b02015-08-12 18:02:49 +0000145 if len(test_name) > test_name_len.value:
146 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000147 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000148 sys.stdout.flush()
149 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000150
Chaoren Linb6325d02015-08-12 18:02:54 +0000151
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000152def parse_test_results(output):
153 passes = 0
154 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000155 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000156 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000157 pass_count = re.search("^RESULT:.*([0-9]+) passes",
158 result, re.MULTILINE)
159 fail_count = re.search("^RESULT:.*([0-9]+) failures",
160 result, re.MULTILINE)
161 error_count = re.search("^RESULT:.*([0-9]+) errors",
162 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000163 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
164 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000165 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000166 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000167 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000168 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000169 if unexpected_success_count is not None:
170 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000171 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000172 failures = failures + int(error_count.group(1))
173 pass
Zachary Turner4cceca72015-08-14 16:45:32 +0000174 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000175
Chaoren Linb6325d02015-08-12 18:02:54 +0000176
Todd Fialada817b62015-09-22 18:05:11 +0000177def create_new_process_group():
178 """Creates a new process group for the process."""
179 os.setpgid(os.getpid(), os.getpid())
180
181
Todd Fiala8cbeed32015-09-08 22:22:33 +0000182def call_with_timeout(command, timeout, name, inferior_pid_events):
Todd Fiala68615ce2015-09-15 21:38:04 +0000183 """Run command with a timeout if possible.
184 -s QUIT will create a coredump if they are enabled on your system
185 """
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000186 process = None
187 if timeout_command and timeout != "0":
188 command = [timeout_command, '-s', 'QUIT', timeout] + command
Todd Fiala68615ce2015-09-15 21:38:04 +0000189
Todd Fiala33896a92015-09-18 21:01:13 +0000190 if GET_WORKER_INDEX is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000191 try:
192 worker_index = GET_WORKER_INDEX()
193 command.extend([
Todd Fiala40b180e2015-09-18 23:46:30 +0000194 "--event-add-entries", "worker_index={}:int".format(worker_index)])
Todd Fialae83f1402015-09-18 22:45:31 +0000195 except:
196 # Ctrl-C does bad things to multiprocessing.Manager.dict() lookup.
197 pass
198
Chaoren Linb6325d02015-08-12 18:02:54 +0000199 # Specifying a value for close_fds is unsupported on Windows when using
200 # subprocess.PIPE
Zachary Turnerdc494d52015-02-07 00:14:55 +0000201 if os.name != "nt":
Chaoren Linb6325d02015-08-12 18:02:54 +0000202 process = subprocess.Popen(command,
203 stdin=subprocess.PIPE,
204 stdout=subprocess.PIPE,
205 stderr=subprocess.PIPE,
Todd Fialada817b62015-09-22 18:05:11 +0000206 close_fds=True,
207 preexec_fn=create_new_process_group)
Zachary Turnerdc494d52015-02-07 00:14:55 +0000208 else:
Chaoren Linb6325d02015-08-12 18:02:54 +0000209 process = subprocess.Popen(command,
210 stdin=subprocess.PIPE,
211 stdout=subprocess.PIPE,
212 stderr=subprocess.PIPE)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000213 inferior_pid = process.pid
214 if inferior_pid_events:
215 inferior_pid_events.put_nowait(('created', inferior_pid))
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000216 output = process.communicate()
Todd Fiala68615ce2015-09-15 21:38:04 +0000217
218 # The inferior should now be entirely wrapped up.
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000219 exit_status = process.returncode
Todd Fiala68615ce2015-09-15 21:38:04 +0000220 if exit_status is None:
221 raise Exception(
222 "no exit status available after the inferior dotest.py "
223 "should have completed")
224
Todd Fiala8cbeed32015-09-08 22:22:33 +0000225 if inferior_pid_events:
226 inferior_pid_events.put_nowait(('destroyed', inferior_pid))
227
Zachary Turner4cceca72015-08-14 16:45:32 +0000228 passes, failures, unexpected_successes = parse_test_results(output)
Zachary Turner38e64172015-08-10 17:46:11 +0000229 if exit_status == 0:
Chaoren Linb6325d02015-08-12 18:02:54 +0000230 # stdout does not have any useful information from 'dotest.py',
231 # only stderr does.
Zachary Turner38e64172015-08-10 17:46:11 +0000232 report_test_pass(name, output[1])
233 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000234 # TODO need to differentiate a failing test from a run that
235 # was broken out of by a SIGTERM/SIGKILL, reporting those as
236 # an error. If a signal-based completion, need to call that
237 # an error.
Zachary Turner38e64172015-08-10 17:46:11 +0000238 report_test_failure(name, command, output[1])
Zachary Turner4cceca72015-08-14 16:45:32 +0000239 return name, exit_status, passes, failures, unexpected_successes
Johnny Chene8d9dc62011-10-31 19:04:07 +0000240
Chaoren Linb6325d02015-08-12 18:02:54 +0000241
Todd Fiala8cbeed32015-09-08 22:22:33 +0000242def process_dir(root, files, test_root, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000243 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000244 results = []
Steve Puccibefe2b12014-03-07 00:01:11 +0000245 for name in files:
Zachary Turnerf6896b02015-01-05 19:37:03 +0000246 script_file = os.path.join(test_root, "dotest.py")
Zachary Turnerf6896b02015-01-05 19:37:03 +0000247 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000248 dotest_argv +
Todd Fialafed95662015-09-03 18:58:44 +0000249 ["--inferior", "-p", name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000250
251 timeout_name = os.path.basename(os.path.splitext(name)[0]).upper()
252
Chaoren Linb6325d02015-08-12 18:02:54 +0000253 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
254 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000255
Todd Fiala8cbeed32015-09-08 22:22:33 +0000256 results.append(call_with_timeout(
257 command, timeout, name, inferior_pid_events))
Vince Harron17f429f2014-12-13 00:08:19 +0000258
Zachary Turner4cceca72015-08-14 16:45:32 +0000259 # result = (name, status, passes, failures, unexpected_successes)
260 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000261 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000262 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000263 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000264 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000265 if status != ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000266 unexpected_passes = [name for name, _, _, _, unexpected_successes in results
267 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000268
Chaoren Line80372a2015-08-12 18:02:53 +0000269 pass_count = sum([result[2] for result in results])
270 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000271
Zachary Turner4cceca72015-08-14 16:45:32 +0000272 return (timed_out, passed, failed, unexpected_passes, pass_count, fail_count)
Steve Puccibefe2b12014-03-07 00:01:11 +0000273
274in_q = None
275out_q = None
276
Chaoren Linb6325d02015-08-12 18:02:54 +0000277
Todd Fiala8cbeed32015-09-08 22:22:33 +0000278def process_dir_worker_multiprocessing(
279 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000280 a_dotest_options, job_queue, result_queue, inferior_pid_events,
281 worker_index_map):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000282 """Worker thread main loop when in multiprocessing mode.
Steve Puccibefe2b12014-03-07 00:01:11 +0000283 Takes one directory specification at a time and works on it."""
Todd Fiala8cbeed32015-09-08 22:22:33 +0000284
285 # Shut off interrupt handling in the child process.
286 signal.signal(signal.SIGINT, signal.SIG_IGN)
Todd Fiala1cc97b42015-09-21 05:42:26 +0000287 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000288
289 # Setup the global state for the worker process.
290 setup_global_variables(
291 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000292 a_dotest_options, worker_index_map)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000293
294 # Keep grabbing entries from the queue until done.
295 while not job_queue.empty():
296 try:
297 job = job_queue.get(block=False)
298 result = process_dir(job[0], job[1], job[2], job[3],
299 inferior_pid_events)
300 result_queue.put(result)
301 except Queue.Empty:
302 # Fine, we're done.
303 pass
Steve Puccibefe2b12014-03-07 00:01:11 +0000304
Chaoren Linb6325d02015-08-12 18:02:54 +0000305
Todd Fiala8cbeed32015-09-08 22:22:33 +0000306def process_dir_worker_multiprocessing_pool(args):
307 return process_dir(*args)
308
309
Todd Fiala68615ce2015-09-15 21:38:04 +0000310def process_dir_worker_threading(job_queue, result_queue, inferior_pid_events):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000311 """Worker thread main loop when in threading mode.
312
313 This one supports the hand-rolled pooling support.
314
315 Takes one directory specification at a time and works on it."""
316
317 # Keep grabbing entries from the queue until done.
318 while not job_queue.empty():
319 try:
320 job = job_queue.get(block=False)
321 result = process_dir(job[0], job[1], job[2], job[3],
322 inferior_pid_events)
323 result_queue.put(result)
324 except Queue.Empty:
325 # Fine, we're done.
326 pass
327
328
329def process_dir_worker_threading_pool(args):
330 return process_dir(*args)
331
332
333def process_dir_mapper_inprocess(args):
334 """Map adapter for running the subprocess-based, non-threaded test runner.
335
336 @param args the process work item tuple
337 @return the test result tuple
338 """
339 return process_dir(*args)
340
341
342def collect_active_pids_from_pid_events(event_queue):
343 """
344 Returns the set of what should be active inferior pids based on
345 the event stream.
346
347 @param event_queue a multiprocessing.Queue containing events of the
348 form:
349 ('created', pid)
350 ('destroyed', pid)
351
352 @return set of inferior dotest.py pids activated but never completed.
353 """
354 active_pid_set = set()
355 while not event_queue.empty():
356 pid_event = event_queue.get_nowait()
357 if pid_event[0] == 'created':
358 active_pid_set.add(pid_event[1])
359 elif pid_event[0] == 'destroyed':
360 active_pid_set.remove(pid_event[1])
361 return active_pid_set
362
363
364def kill_all_worker_processes(workers, inferior_pid_events):
365 """
366 Kills all specified worker processes and their process tree.
367
368 @param workers a list of multiprocess.Process worker objects.
369 @param inferior_pid_events a multiprocess.Queue that contains
370 all inferior create and destroy events. Used to construct
371 the list of child pids still outstanding that need to be killed.
372 """
373 for worker in workers:
374 worker.terminate()
375 worker.join()
376
377 # Add all the child test pids created.
378 active_pid_set = collect_active_pids_from_pid_events(
379 inferior_pid_events)
380 for inferior_pid in active_pid_set:
381 print "killing inferior pid {}".format(inferior_pid)
382 os.kill(inferior_pid, signal.SIGKILL)
383
384
385def kill_all_worker_threads(workers, inferior_pid_events):
386 """
387 Kills all specified worker threads and their process tree.
388
389 @param workers a list of multiprocess.Process worker objects.
390 @param inferior_pid_events a multiprocess.Queue that contains
391 all inferior create and destroy events. Used to construct
392 the list of child pids still outstanding that need to be killed.
393 """
394
395 # Add all the child test pids created.
396 active_pid_set = collect_active_pids_from_pid_events(
397 inferior_pid_events)
398 for inferior_pid in active_pid_set:
399 print "killing inferior pid {}".format(inferior_pid)
400 os.kill(inferior_pid, signal.SIGKILL)
401
402 # We don't have a way to nuke the threads. However, since we killed
403 # all the inferiors, and we drained the job queue, this will be
404 # good enough. Wait cleanly for each worker thread to wrap up.
405 for worker in workers:
406 worker.join()
407
408
409def find_test_files_in_dir_tree(dir_root, found_func):
410 """Calls found_func for all the test files in the given dir hierarchy.
411
412 @param dir_root the path to the directory to start scanning
413 for test files. All files in this directory and all its children
414 directory trees will be searched.
415
416 @param found_func a callable object that will be passed
417 the parent directory (relative to dir_root) and the list of
418 test files from within that directory.
419 """
420 for root, _, files in os.walk(dir_root, topdown=False):
421 def is_test_filename(test_dir, base_filename):
422 """Returns True if the given filename matches the test name format.
423
424 @param test_dir the directory to check. Should be absolute or
425 relative to current working directory.
426
427 @param base_filename the base name of the filename to check for a
428 dherence to the python test case filename format.
429
430 @return True if name matches the python test case filename format.
431 """
432 # Not interested in symbolically linked files.
433 if os.path.islink(os.path.join(test_dir, base_filename)):
434 return False
435 # Only interested in test files with the "Test*.py" naming pattern.
436 return (base_filename.startswith("Test") and
437 base_filename.endswith(".py"))
438
439 tests = [filename for filename in files
440 if is_test_filename(root, filename)]
441 if tests:
442 found_func(root, tests)
443
444
445def initialize_global_vars_common(num_threads, test_work_items):
446 global total_tests, test_counter, test_name_len
Greg Clayton1827fc22015-09-19 00:39:09 +0000447
Todd Fiala8cbeed32015-09-08 22:22:33 +0000448 total_tests = sum([len(item[1]) for item in test_work_items])
449 test_counter = multiprocessing.Value('i', 0)
450 test_name_len = multiprocessing.Value('i', 0)
Greg Clayton1827fc22015-09-19 00:39:09 +0000451 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
452 print >> sys.stderr, "Testing: %d test suites, %d thread%s" % (
453 total_tests, num_threads, (num_threads > 1) * "s")
Todd Fiala8cbeed32015-09-08 22:22:33 +0000454 update_progress()
455
456
457def initialize_global_vars_multiprocessing(num_threads, test_work_items):
458 # Initialize the global state we'll use to communicate with the
459 # rest of the flat module.
460 global output_lock
461 output_lock = multiprocessing.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000462
Todd Fiala8cbeed32015-09-08 22:22:33 +0000463 initialize_global_vars_common(num_threads, test_work_items)
464
465
466def initialize_global_vars_threading(num_threads, test_work_items):
Todd Fiala33896a92015-09-18 21:01:13 +0000467 """Initializes global variables used in threading mode.
468 @param num_threads specifies the number of workers used.
469 @param test_work_items specifies all the work items
470 that will be processed.
471 """
Todd Fiala8cbeed32015-09-08 22:22:33 +0000472 # Initialize the global state we'll use to communicate with the
473 # rest of the flat module.
474 global output_lock
475 output_lock = threading.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000476
477 index_lock = threading.RLock()
478 index_map = {}
479
480 def get_worker_index_threading():
481 """Returns a 0-based, thread-unique index for the worker thread."""
482 thread_id = threading.current_thread().ident
483 with index_lock:
484 if thread_id not in index_map:
485 index_map[thread_id] = len(index_map)
486 return index_map[thread_id]
487
488
489 global GET_WORKER_INDEX
490 GET_WORKER_INDEX = get_worker_index_threading
491
Todd Fiala8cbeed32015-09-08 22:22:33 +0000492 initialize_global_vars_common(num_threads, test_work_items)
493
494
Todd Fiala68615ce2015-09-15 21:38:04 +0000495def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler):
496 """Provides a main loop that is Ctrl-C protected.
497
498 The main loop calls the main_op_func() repeatedly until done_func()
499 returns true. The ctrl_c_handler() method is called with a single
500 int parameter that contains the number of times the ctrl_c has been
501 hit (starting with 1). The ctrl_c_handler() should mutate whatever
502 it needs to have the done_func() return True as soon as it is desired
503 to exit the loop.
504 """
505 done = False
506 ctrl_c_count = 0
507
508 while not done:
509 try:
510 # See if we're done. Start with done check since it is
511 # the first thing executed after a Ctrl-C handler in the
512 # following loop.
513 done = done_func()
514 if not done:
515 # Run the main op once.
516 main_op_func()
517
518 except KeyboardInterrupt:
519 ctrl_c_count += 1
520 ctrl_c_handler(ctrl_c_count)
521
522
523def pump_workers_and_asyncore_map(workers, asyncore_map):
524 """Prunes out completed workers and maintains the asyncore loop.
525
526 The asyncore loop contains the optional socket listener
527 and handlers. When all workers are complete, this method
528 takes care of stopping the listener. It also runs the
529 asyncore loop for the given async map for 10 iterations.
530
531 @param workers the list of worker Thread/Process instances.
532
533 @param asyncore_map the asyncore threading-aware map that
534 indicates which channels are in use and still alive.
535 """
536
537 # Check on all the workers, removing them from the workers
538 # list as they complete.
539 dead_workers = []
540 for worker in workers:
541 # This non-blocking join call is what allows us
542 # to still receive keyboard interrupts.
543 worker.join(0.01)
544 if not worker.is_alive():
545 dead_workers.append(worker)
546 # Clear out the completed workers
547 for dead_worker in dead_workers:
548 workers.remove(dead_worker)
549
550 # If there are no more workers and there is a listener,
551 # close the listener.
552 global RESULTS_LISTENER_CHANNEL
553 if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None:
554 RESULTS_LISTENER_CHANNEL.close()
555 RESULTS_LISTENER_CHANNEL = None
556
557 # Pump the asyncore map if it isn't empty.
558 if len(asyncore_map) > 0:
559 asyncore.loop(0.1, False, asyncore_map, 10)
560
561
562def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events,
563 stop_all_inferiors_func):
564 """Performs the appropriate ctrl-c action for non-pool parallel test runners
565
566 @param ctrl_c_count starting with 1, indicates the number of times ctrl-c
567 has been intercepted. The value is 1 on the first intercept, 2 on the
568 second, etc.
569
570 @param job_queue a Queue object that contains the work still outstanding
571 (i.e. hasn't been assigned to a worker yet).
572
573 @param workers list of Thread or Process workers.
574
575 @param inferior_pid_events specifies a Queue of inferior process
576 construction and destruction events. Used to build the list of inferior
577 processes that should be killed if we get that far.
578
579 @param stop_all_inferiors_func a callable object that takes the
580 workers and inferior_pid_events parameters (in that order) if a hard
581 stop is to be used on the workers.
582 """
583
584 # Print out which Ctrl-C we're handling.
585 key_name = [
586 "first",
587 "second",
588 "third",
589 "many"]
590
591 if ctrl_c_count < len(key_name):
592 name_index = ctrl_c_count - 1
593 else:
594 name_index = len(key_name) - 1
595 message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index])
596 with output_lock:
597 print message
598
599 if ctrl_c_count == 1:
600 # Remove all outstanding items from the work queue so we stop
601 # doing any more new work.
602 while not job_queue.empty():
603 try:
604 # Just drain it to stop more work from being started.
605 job_queue.get_nowait()
606 except Queue.Empty:
607 pass
608 with output_lock:
609 print "Stopped more work from being started."
610 elif ctrl_c_count == 2:
611 # Try to stop all inferiors, even the ones currently doing work.
612 stop_all_inferiors_func(workers, inferior_pid_events)
613 else:
614 with output_lock:
615 print "All teardown activities kicked off, should finish soon."
616
617
618def workers_and_async_done(workers, async_map):
619 """Returns True if the workers list and asyncore channels are all done.
620
621 @param workers list of workers (threads/processes). These must adhere
622 to the threading Thread or multiprocessing.Process interface.
623
624 @param async_map the threading-aware asyncore channel map to check
625 for live channels.
626
627 @return False if the workers list exists and has any entries in it, or
628 if the async_map exists and has any entries left in it; otherwise, True.
629 """
630 if workers is not None and len(workers) > 0:
631 # We're not done if we still have workers left.
632 return False
633 if async_map is not None and len(async_map) > 0:
634 return False
635 # We're done.
636 return True
637
638
Todd Fiala8cbeed32015-09-08 22:22:33 +0000639def multiprocessing_test_runner(num_threads, test_work_items):
640 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
641
642 This concurrent test runner is based on the multiprocessing
643 library, and rolls its own worker pooling strategy so it
644 can handle Ctrl-C properly.
645
646 This test runner is known to have an issue running on
647 Windows platforms.
648
649 @param num_threads the number of worker processes to use.
650
651 @param test_work_items the iterable of test work item tuples
652 to run.
653 """
654
655 # Initialize our global state.
656 initialize_global_vars_multiprocessing(num_threads, test_work_items)
657
658 # Create jobs.
659 job_queue = multiprocessing.Queue(len(test_work_items))
660 for test_work_item in test_work_items:
661 job_queue.put(test_work_item)
662
663 result_queue = multiprocessing.Queue(len(test_work_items))
664
665 # Create queues for started child pids. Terminating
666 # the multiprocess processes does not terminate the
667 # child processes they spawn. We can remove this tracking
668 # if/when we move to having the multiprocess process directly
669 # perform the test logic. The Queue size needs to be able to
670 # hold 2 * (num inferior dotest.py processes started) entries.
671 inferior_pid_events = multiprocessing.Queue(4096)
672
Todd Fiala33896a92015-09-18 21:01:13 +0000673 # Worker dictionary allows each worker to figure out its worker index.
674 manager = multiprocessing.Manager()
675 worker_index_map = manager.dict()
676
Todd Fiala8cbeed32015-09-08 22:22:33 +0000677 # Create workers. We don't use multiprocessing.Pool due to
678 # challenges with handling ^C keyboard interrupts.
679 workers = []
680 for _ in range(num_threads):
681 worker = multiprocessing.Process(
682 target=process_dir_worker_multiprocessing,
683 args=(output_lock,
684 test_counter,
685 total_tests,
686 test_name_len,
687 dotest_options,
688 job_queue,
689 result_queue,
Todd Fiala33896a92015-09-18 21:01:13 +0000690 inferior_pid_events,
691 worker_index_map))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000692 worker.start()
693 workers.append(worker)
694
Todd Fiala68615ce2015-09-15 21:38:04 +0000695 # Main loop: wait for all workers to finish and wait for
696 # the socket handlers to wrap up.
697 ctrl_c_loop(
698 # Main operation of loop
699 lambda: pump_workers_and_asyncore_map(
700 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000701
Todd Fiala68615ce2015-09-15 21:38:04 +0000702 # Return True when we're done with the main loop.
703 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000704
Todd Fiala68615ce2015-09-15 21:38:04 +0000705 # Indicate what we do when we receive one or more Ctrl-Cs.
706 lambda ctrl_c_count: handle_ctrl_c(
707 ctrl_c_count, job_queue, workers, inferior_pid_events,
708 kill_all_worker_processes))
709
710 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000711 test_results = []
712 while not result_queue.empty():
713 test_results.append(result_queue.get(block=False))
714 return test_results
715
716
Todd Fiala68615ce2015-09-15 21:38:04 +0000717def map_async_run_loop(future, channel_map, listener_channel):
718 """Blocks until the Pool.map_async completes and the channel completes.
719
720 @param future an AsyncResult instance from a Pool.map_async() call.
721
722 @param channel_map the asyncore dispatch channel map that should be pumped.
723 Optional: may be None.
724
725 @param listener_channel the channel representing a listener that should be
726 closed once the map_async results are available.
727
728 @return the results from the async_result instance.
729 """
730 map_results = None
731
732 done = False
733 while not done:
734 # Check if we need to reap the map results.
735 if map_results is None:
736 if future.ready():
737 # Get the results.
738 map_results = future.get()
739
740 # Close the runner process listener channel if we have
741 # one: no more connections will be incoming.
742 if listener_channel is not None:
743 listener_channel.close()
744
745 # Pump the asyncore loop if we have a listener socket.
746 if channel_map is not None:
747 asyncore.loop(0.01, False, channel_map, 10)
748
749 # Figure out if we're done running.
750 done = map_results is not None
751 if channel_map is not None:
752 # We have a runner process async map. Check if it
753 # is complete.
754 if len(channel_map) > 0:
755 # We still have an asyncore channel running. Not done yet.
756 done = False
757
758 return map_results
759
760
Todd Fiala8cbeed32015-09-08 22:22:33 +0000761def multiprocessing_test_runner_pool(num_threads, test_work_items):
762 # Initialize our global state.
763 initialize_global_vars_multiprocessing(num_threads, test_work_items)
764
Todd Fiala33896a92015-09-18 21:01:13 +0000765 manager = multiprocessing.Manager()
766 worker_index_map = manager.dict()
767
Todd Fiala8cbeed32015-09-08 22:22:33 +0000768 pool = multiprocessing.Pool(
769 num_threads,
770 initializer=setup_global_variables,
771 initargs=(output_lock, test_counter, total_tests, test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000772 dotest_options, worker_index_map))
Todd Fiala68615ce2015-09-15 21:38:04 +0000773
774 # Start the map operation (async mode).
775 map_future = pool.map_async(
776 process_dir_worker_multiprocessing_pool, test_work_items)
777 return map_async_run_loop(
778 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000779
780
781def threading_test_runner(num_threads, test_work_items):
782 """Provides hand-wrapped pooling threading-based test runner adapter
783 with Ctrl-C support.
784
785 This concurrent test runner is based on the threading
786 library, and rolls its own worker pooling strategy so it
787 can handle Ctrl-C properly.
788
789 @param num_threads the number of worker processes to use.
790
791 @param test_work_items the iterable of test work item tuples
792 to run.
793 """
794
795 # Initialize our global state.
796 initialize_global_vars_threading(num_threads, test_work_items)
797
798 # Create jobs.
799 job_queue = Queue.Queue()
800 for test_work_item in test_work_items:
801 job_queue.put(test_work_item)
802
803 result_queue = Queue.Queue()
804
805 # Create queues for started child pids. Terminating
806 # the threading threads does not terminate the
807 # child processes they spawn.
808 inferior_pid_events = Queue.Queue()
809
810 # Create workers. We don't use multiprocessing.pool.ThreadedPool
811 # due to challenges with handling ^C keyboard interrupts.
812 workers = []
813 for _ in range(num_threads):
814 worker = threading.Thread(
815 target=process_dir_worker_threading,
Todd Fiala68615ce2015-09-15 21:38:04 +0000816 args=(job_queue,
Todd Fiala8cbeed32015-09-08 22:22:33 +0000817 result_queue,
818 inferior_pid_events))
819 worker.start()
820 workers.append(worker)
821
Todd Fiala68615ce2015-09-15 21:38:04 +0000822 # Main loop: wait for all workers to finish and wait for
823 # the socket handlers to wrap up.
824 ctrl_c_loop(
825 # Main operation of loop
826 lambda: pump_workers_and_asyncore_map(
827 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000828
Todd Fiala68615ce2015-09-15 21:38:04 +0000829 # Return True when we're done with the main loop.
830 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000831
Todd Fiala68615ce2015-09-15 21:38:04 +0000832 # Indicate what we do when we receive one or more Ctrl-Cs.
833 lambda ctrl_c_count: handle_ctrl_c(
834 ctrl_c_count, job_queue, workers, inferior_pid_events,
835 kill_all_worker_threads))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000836
Todd Fiala68615ce2015-09-15 21:38:04 +0000837 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000838 test_results = []
839 while not result_queue.empty():
840 test_results.append(result_queue.get(block=False))
841 return test_results
842
843
844def threading_test_runner_pool(num_threads, test_work_items):
845 # Initialize our global state.
846 initialize_global_vars_threading(num_threads, test_work_items)
847
Todd Fiala68615ce2015-09-15 21:38:04 +0000848 pool = multiprocessing.pool.ThreadPool(num_threads)
849 map_future = pool.map_async(
850 process_dir_worker_threading_pool, test_work_items)
851
852 return map_async_run_loop(
853 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
854
855
856def asyncore_run_loop(channel_map):
857 try:
858 asyncore.loop(None, False, channel_map)
859 except:
860 # Swallow it, we're seeing:
861 # error: (9, 'Bad file descriptor')
862 # when the listener channel is closed. Shouldn't be the case.
863 pass
Todd Fiala8cbeed32015-09-08 22:22:33 +0000864
865
866def inprocess_exec_test_runner(test_work_items):
867 # Initialize our global state.
868 initialize_global_vars_multiprocessing(1, test_work_items)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000869
Todd Fiala33896a92015-09-18 21:01:13 +0000870 # We're always worker index 0
871 global GET_WORKER_INDEX
872 GET_WORKER_INDEX = lambda: 0
873
Todd Fiala68615ce2015-09-15 21:38:04 +0000874 # Run the listener and related channel maps in a separate thread.
875 # global RUNNER_PROCESS_ASYNC_MAP
876 global RESULTS_LISTENER_CHANNEL
877 if RESULTS_LISTENER_CHANNEL is not None:
878 socket_thread = threading.Thread(
879 target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP))
880 socket_thread.start()
881
882 # Do the work.
883 test_results = map(process_dir_mapper_inprocess, test_work_items)
884
885 # If we have a listener channel, shut it down here.
886 if RESULTS_LISTENER_CHANNEL is not None:
887 # Close down the channel.
888 RESULTS_LISTENER_CHANNEL.close()
889 RESULTS_LISTENER_CHANNEL = None
890
891 # Wait for the listener and handlers to complete.
892 socket_thread.join()
893
894 return test_results
Todd Fiala8cbeed32015-09-08 22:22:33 +0000895
896def walk_and_invoke(test_directory, test_subdir, dotest_argv,
897 test_runner_func):
Steve Puccibefe2b12014-03-07 00:01:11 +0000898 """Look for matched files and invoke test driver on each one.
899 In single-threaded mode, each test driver is invoked directly.
900 In multi-threaded mode, submit each test driver to a worker
Vince Harrone06a7a82015-05-12 23:12:19 +0000901 queue, and then wait for all to complete.
902
903 test_directory - lldb/test/ directory
Chaoren Linb6325d02015-08-12 18:02:54 +0000904 test_subdir - lldb/test/ or a subfolder with the tests we're interested in
905 running
Vince Harrone06a7a82015-05-12 23:12:19 +0000906 """
Todd Fiala68615ce2015-09-15 21:38:04 +0000907 # The async_map is important to keep all thread-related asyncore
908 # channels distinct when we call asyncore.loop() later on.
909 global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP
910 RUNNER_PROCESS_ASYNC_MAP = {}
911
912 # If we're outputting side-channel test results, create the socket
913 # listener channel and tell the inferior to send results to the
914 # port on which we'll be listening.
915 if RESULTS_FORMATTER is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000916 forwarding_func = RESULTS_FORMATTER.handle_event
Todd Fiala68615ce2015-09-15 21:38:04 +0000917 RESULTS_LISTENER_CHANNEL = (
918 dotest_channels.UnpicklingForwardingListenerChannel(
919 RUNNER_PROCESS_ASYNC_MAP, "localhost", 0, forwarding_func))
920 dotest_argv.append("--results-port")
921 dotest_argv.append(str(RESULTS_LISTENER_CHANNEL.address[1]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000922
923 # Collect the test files that we'll run.
924 test_work_items = []
Todd Fiala8cbeed32015-09-08 22:22:33 +0000925 find_test_files_in_dir_tree(
926 test_subdir, lambda testdir, test_files: test_work_items.append([
927 test_subdir, test_files, test_directory, dotest_argv, None]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000928
Todd Fiala8cbeed32015-09-08 22:22:33 +0000929 # Convert test work items into test results using whatever
930 # was provided as the test run function.
931 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +0000932
Todd Fiala8cbeed32015-09-08 22:22:33 +0000933 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +0000934 timed_out = sum([result[0] for result in test_results], [])
935 passed = sum([result[1] for result in test_results], [])
936 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +0000937 unexpected_successes = sum([result[3] for result in test_results], [])
938 pass_count = sum([result[4] for result in test_results])
939 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +0000940
Todd Fiala8cbeed32015-09-08 22:22:33 +0000941 return (timed_out, passed, failed, unexpected_successes, pass_count,
942 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +0000943
Chaoren Linb6325d02015-08-12 18:02:54 +0000944
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000945def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +0000946 # returns a set of test filenames that might timeout
947 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +0000948 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000949 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +0000950 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000951 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000952 m = re.search(r'remote-(\w+)', platform_name)
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000953 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +0000954
955 expected_timeout = set()
956
957 if target.startswith("linux"):
958 expected_timeout |= {
Chaoren Lin0b8bb3d2015-07-22 20:52:17 +0000959 "TestProcessAttach.py",
Vince Harron06381732015-05-12 23:10:36 +0000960 "TestConnectRemote.py",
961 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +0000962 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +0000963 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +0000964
965 # Times out in ~10% of the times on the build bot
966 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +0000967 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +0000968 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +0000969 "TestThreadStepOut.py",
970 }
971 elif target.startswith("android"):
972 expected_timeout |= {
973 "TestExitDuringStep.py",
974 "TestHelloWorld.py",
975 }
Chaoren Linfebef1b2015-08-19 17:22:12 +0000976 if host.startswith("win32"):
977 expected_timeout |= {
978 "TestEvents.py",
979 "TestThreadStates.py",
980 }
Ed Maste4dd8fba2015-05-14 16:25:52 +0000981 elif target.startswith("freebsd"):
982 expected_timeout |= {
983 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +0000984 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +0000985 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +0000986 "TestWatchpointConditionAPI.py",
987 }
Vince Harron0f173ac2015-05-18 19:36:33 +0000988 elif target.startswith("darwin"):
989 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +0000990 # times out on MBP Retina, Mid 2012
991 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +0000992 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +0000993 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +0000994 }
Vince Harron06381732015-05-12 23:10:36 +0000995 return expected_timeout
996
Chaoren Linb6325d02015-08-12 18:02:54 +0000997
Pavel Labathfad30cf2015-06-29 14:16:51 +0000998def getDefaultTimeout(platform_name):
999 if os.getenv("LLDB_TEST_TIMEOUT"):
1000 return os.getenv("LLDB_TEST_TIMEOUT")
1001
1002 if platform_name is None:
1003 platform_name = sys.platform
1004
1005 if platform_name.startswith("remote-"):
1006 return "10m"
1007 else:
1008 return "4m"
1009
Chaoren Linb6325d02015-08-12 18:02:54 +00001010
Vince Harron0b9dbb52015-05-21 18:18:52 +00001011def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +00001012 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +00001013 os.utime(fname, times)
1014
Chaoren Linb6325d02015-08-12 18:02:54 +00001015
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001016def find(pattern, path):
1017 result = []
1018 for root, dirs, files in os.walk(path):
1019 for name in files:
1020 if fnmatch.fnmatch(name, pattern):
1021 result.append(os.path.join(root, name))
1022 return result
1023
Chaoren Linb6325d02015-08-12 18:02:54 +00001024
Todd Fiala8cbeed32015-09-08 22:22:33 +00001025def get_test_runner_strategies(num_threads):
1026 """Returns the test runner strategies by name in a dictionary.
1027
1028 @param num_threads specifies the number of threads/processes
1029 that will be used for concurrent test runners.
1030
1031 @return dictionary with key as test runner strategy name and
1032 value set to a callable object that takes the test work item
1033 and returns a test result tuple.
1034 """
1035 return {
1036 # multiprocessing supports ctrl-c and does not use
1037 # multiprocessing.Pool.
1038 "multiprocessing":
1039 (lambda work_items: multiprocessing_test_runner(
1040 num_threads, work_items)),
1041
1042 # multiprocessing-pool uses multiprocessing.Pool but
1043 # does not support Ctrl-C.
1044 "multiprocessing-pool":
1045 (lambda work_items: multiprocessing_test_runner_pool(
1046 num_threads, work_items)),
1047
1048 # threading uses a hand-rolled worker pool much
1049 # like multiprocessing, but instead uses in-process
1050 # worker threads. This one supports Ctrl-C.
1051 "threading":
1052 (lambda work_items: threading_test_runner(num_threads, work_items)),
1053
1054 # threading-pool uses threading for the workers (in-process)
1055 # and uses the multiprocessing.pool thread-enabled pool.
Todd Fiala68615ce2015-09-15 21:38:04 +00001056 # This does not properly support Ctrl-C.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001057 "threading-pool":
1058 (lambda work_items: threading_test_runner_pool(
1059 num_threads, work_items)),
1060
1061 # serial uses the subprocess-based, single process
1062 # test runner. This provides process isolation but
Todd Fiala68615ce2015-09-15 21:38:04 +00001063 # no concurrent test execution.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001064 "serial":
1065 inprocess_exec_test_runner
1066 }
1067
1068
Todd Fiala68615ce2015-09-15 21:38:04 +00001069def _remove_option(args, option_name, removal_count):
1070 """Removes option and related option arguments from args array.
1071 @param args the array of command line arguments (in/out)
1072 @param option_name the full command line representation of the
1073 option that will be removed (including '--' or '-').
1074 @param the count of elements to remove. A value of 1 will remove
1075 just the found option, while 2 will remove the option and its first
1076 argument.
1077 """
1078 try:
1079 index = args.index(option_name)
1080 # Handle the exact match case.
1081 del args[index:index+removal_count]
1082 return
1083 except ValueError:
1084 # Thanks to argparse not handling options with known arguments
1085 # like other options parsing libraries (see
1086 # https://bugs.python.org/issue9334), we need to support the
1087 # --results-formatter-options={second-level-arguments} (note
1088 # the equal sign to fool the first-level arguments parser into
1089 # not treating the second-level arguments as first-level
1090 # options). We're certainly at risk of getting this wrong
1091 # since now we're forced into the business of trying to figure
1092 # out what is an argument (although I think this
1093 # implementation will suffice).
1094 regex_string = "^" + option_name + "="
1095 regex = re.compile(regex_string)
1096 for index in range(len(args)):
1097 match = regex.match(args[index])
1098 if match:
Todd Fiala68615ce2015-09-15 21:38:04 +00001099 del args[index]
1100 return
1101 print "failed to find regex '{}'".format(regex_string)
1102
1103 # We didn't find the option but we should have.
1104 raise Exception("failed to find option '{}' in args '{}'".format(
1105 option_name, args))
1106
1107
1108def adjust_inferior_options(dotest_argv):
1109 """Adjusts the commandline args array for inferiors.
1110
1111 This method adjusts the inferior dotest commandline options based
1112 on the parallel test runner's options. Some of the inferior options
1113 will need to change to properly handle aggregation functionality.
1114 """
1115 global dotest_options
1116
1117 # If we don't have a session directory, create one.
1118 if not dotest_options.s:
1119 # no session log directory, we need to add this to prevent
1120 # every dotest invocation from creating its own directory
1121 import datetime
1122 # The windows platforms don't like ':' in the pathname.
1123 timestamp_started = datetime.datetime.now().strftime("%F-%H_%M_%S")
1124 dotest_argv.append('-s')
1125 dotest_argv.append(timestamp_started)
1126 dotest_options.s = timestamp_started
1127
1128 # Adjust inferior results formatter options - if the parallel
1129 # test runner is collecting into the user-specified test results,
1130 # we'll have inferiors spawn with the --results-port option and
1131 # strip the original test runner options.
1132 if dotest_options.results_file is not None:
1133 _remove_option(dotest_argv, "--results-file", 2)
1134 if dotest_options.results_port is not None:
1135 _remove_option(dotest_argv, "--results-port", 2)
1136 if dotest_options.results_formatter is not None:
1137 _remove_option(dotest_argv, "--results-formatter", 2)
1138 if dotest_options.results_formatter_options is not None:
1139 _remove_option(dotest_argv, "--results-formatter-options", 2)
1140
Todd Fiala33896a92015-09-18 21:01:13 +00001141 # Remove test runner name if present.
1142 if dotest_options.test_runner_name is not None:
1143 _remove_option(dotest_argv, "--test-runner-name", 2)
1144
1145
Todd Fiala8cbeed32015-09-08 22:22:33 +00001146def main(print_details_on_success, num_threads, test_subdir,
Todd Fiala68615ce2015-09-15 21:38:04 +00001147 test_runner_name, results_formatter):
Todd Fialafed95662015-09-03 18:58:44 +00001148 """Run dotest.py in inferior mode in parallel.
1149
1150 @param print_details_on_success the parsed value of the output-on-success
1151 command line argument. When True, details of a successful dotest inferior
1152 are printed even when everything succeeds. The normal behavior is to
1153 not print any details when all the inferior tests pass.
1154
1155 @param num_threads the parsed value of the num-threads command line
1156 argument.
1157
1158 @param test_subdir optionally specifies a subdir to limit testing
1159 within. May be None if the entire test tree is to be used. This subdir
1160 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001161
1162 @param test_runner_name if specified, contains the test runner
1163 name which selects the strategy used to run the isolated and
1164 optionally concurrent test runner. Specify None to allow the
1165 system to choose the most appropriate test runner given desired
1166 thread count and OS type.
1167
Todd Fiala68615ce2015-09-15 21:38:04 +00001168 @param results_formatter if specified, provides the TestResultsFormatter
1169 instance that will format and output test result data from the
1170 side-channel test results. When specified, inferior dotest calls
1171 will send test results side-channel data over a socket to the parallel
1172 test runner, which will forward them on to results_formatter.
Todd Fialafed95662015-09-03 18:58:44 +00001173 """
1174
Todd Fiala1cc97b42015-09-21 05:42:26 +00001175 # Do not shut down on sighup.
1176 signal.signal(signal.SIGHUP, signal.SIG_IGN)
1177
Todd Fialafed95662015-09-03 18:58:44 +00001178 dotest_argv = sys.argv[1:]
1179
Greg Claytonb0d148e2015-09-21 17:25:01 +00001180 global output_on_success, RESULTS_FORMATTER
Todd Fialafed95662015-09-03 18:58:44 +00001181 output_on_success = print_details_on_success
Todd Fiala68615ce2015-09-15 21:38:04 +00001182 RESULTS_FORMATTER = results_formatter
Todd Fialafed95662015-09-03 18:58:44 +00001183
Vince Harrond5fa1022015-05-10 15:24:12 +00001184 # We can't use sys.path[0] to determine the script directory
1185 # because it doesn't work under a debugger
Vince Harron8994fed2015-05-22 19:49:23 +00001186 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +00001187 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +00001188 dotest_options = dotest_args.parse_args(parser, dotest_argv)
1189
Todd Fiala68615ce2015-09-15 21:38:04 +00001190 adjust_inferior_options(dotest_argv)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001191
1192 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +00001193
Vince Harrone06a7a82015-05-12 23:12:19 +00001194 # The root directory was specified on the command line
Todd Fiala68615ce2015-09-15 21:38:04 +00001195 test_directory = os.path.dirname(os.path.realpath(__file__))
Todd Fialafed95662015-09-03 18:58:44 +00001196 if test_subdir and len(test_subdir) > 0:
1197 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +00001198 else:
Todd Fialafed95662015-09-03 18:58:44 +00001199 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +00001200
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001201 # clean core files in test tree from previous runs (Linux)
1202 cores = find('core.*', test_subdir)
1203 for core in cores:
1204 os.unlink(core)
1205
Daniel Maleab42556f2013-04-19 18:32:53 +00001206 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +00001207
1208 # Figure out which testrunner strategy we'll use.
1209 runner_strategies_by_name = get_test_runner_strategies(num_threads)
1210
1211 # If the user didn't specify a test runner strategy, determine
1212 # the default now based on number of threads and OS type.
1213 if not test_runner_name:
1214 if num_threads == 1:
1215 # Use the serial runner.
1216 test_runner_name = "serial"
1217 elif os.name == "nt":
1218 # Currently the multiprocessing test runner with ctrl-c
1219 # support isn't running correctly on nt. Use the pool
1220 # support without ctrl-c.
1221 test_runner_name = "multiprocessing-pool"
1222 else:
1223 # For everyone else, use the ctrl-c-enabled
1224 # multiprocessing support.
1225 test_runner_name = "multiprocessing"
1226
1227 if test_runner_name not in runner_strategies_by_name:
1228 raise Exception("specified testrunner name '{}' unknown. "
1229 "Valid choices: {}".format(
1230 test_runner_name,
1231 runner_strategies_by_name.keys()))
1232 test_runner_func = runner_strategies_by_name[test_runner_name]
1233
1234 summary_results = walk_and_invoke(
1235 test_directory, test_subdir, dotest_argv, test_runner_func)
1236
1237 (timed_out, passed, failed, unexpected_successes, pass_count,
1238 fail_count) = summary_results
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +00001239
Todd Fialade9a44e2015-09-22 00:15:50 +00001240 # The results formatter - if present - is done now. Tell it to
1241 # terminate.
1242 if results_formatter is not None:
1243 results_formatter.send_terminate_as_needed()
1244
Vince Harron17f429f2014-12-13 00:08:19 +00001245 timed_out = set(timed_out)
Chaoren Line80372a2015-08-12 18:02:53 +00001246 num_test_files = len(passed) + len(failed)
1247 num_test_cases = pass_count + fail_count
Daniel Maleab42556f2013-04-19 18:32:53 +00001248
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001249 # move core files into session dir
1250 cores = find('core.*', test_subdir)
1251 for core in cores:
1252 dst = core.replace(test_directory, "")[1:]
1253 dst = dst.replace(os.path.sep, "-")
1254 os.rename(core, os.path.join(session_dir, dst))
1255
Vince Harron06381732015-05-12 23:10:36 +00001256 # remove expected timeouts from failures
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001257 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
Vince Harron06381732015-05-12 23:10:36 +00001258 for xtime in expected_timeout:
1259 if xtime in timed_out:
1260 timed_out.remove(xtime)
1261 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001262 result = "ExpectedTimeout"
1263 elif xtime in passed:
1264 result = "UnexpectedCompletion"
1265 else:
1266 result = None # failed
1267
1268 if result:
1269 test_name = os.path.splitext(xtime)[0]
1270 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +00001271
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +00001272 print
Chaoren Lin5a59e462015-08-12 18:02:51 +00001273 sys.stdout.write("Ran %d test suites" % num_test_files)
1274 if num_test_files > 0:
1275 sys.stdout.write(" (%d failed) (%f%%)" % (
1276 len(failed), 100.0 * len(failed) / num_test_files))
1277 print
Chaoren Line80372a2015-08-12 18:02:53 +00001278 sys.stdout.write("Ran %d test cases" % num_test_cases)
1279 if num_test_cases > 0:
Chaoren Lin5a59e462015-08-12 18:02:51 +00001280 sys.stdout.write(" (%d failed) (%f%%)" % (
Chaoren Line80372a2015-08-12 18:02:53 +00001281 fail_count, 100.0 * fail_count / num_test_cases))
Chaoren Lin5a59e462015-08-12 18:02:51 +00001282 print
Zachary Turner4cceca72015-08-14 16:45:32 +00001283 exit_code = 0
1284
Daniel Maleacbaef262013-02-15 21:31:37 +00001285 if len(failed) > 0:
Shawn Best13491c42014-10-22 19:29:00 +00001286 failed.sort()
Ying Chen10ed1a92015-05-28 23:51:49 +00001287 print "Failing Tests (%d)" % len(failed)
Daniel Maleacbaef262013-02-15 21:31:37 +00001288 for f in failed:
Vince Harron17f429f2014-12-13 00:08:19 +00001289 print "%s: LLDB (suite) :: %s (%s)" % (
1290 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
1291 )
Zachary Turner4cceca72015-08-14 16:45:32 +00001292 exit_code = 1
1293
1294 if len(unexpected_successes) > 0:
1295 unexpected_successes.sort()
1296 print "\nUnexpected Successes (%d)" % len(unexpected_successes)
1297 for u in unexpected_successes:
1298 print "UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info)
1299
1300 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001301
1302if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +00001303 sys.stderr.write(
1304 "error: dosep.py no longer supports being called directly. "
1305 "Please call dotest.py directly. The dosep.py-specific arguments "
1306 "have been added under the Parallel processing arguments.\n")
1307 sys.exit(128)