blob: e5ab2aa4b44b7d51e6d1ab55db461a5d5736aca7 [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
Todd Fiala83c32e32015-09-22 21:19:40 +000036import distutils.version
Vince Harrondcc2b9f2015-05-27 04:40:36 +000037import fnmatch
Todd Fiala8cbeed32015-09-08 22:22:33 +000038import multiprocessing
39import multiprocessing.pool
40import os
Todd Fiala3f0a3602014-07-08 06:42:37 +000041import platform
Todd Fiala8cbeed32015-09-08 22:22:33 +000042import Queue
Vince Harron06381732015-05-12 23:10:36 +000043import re
Todd Fiala8cbeed32015-09-08 22:22:33 +000044import signal
Vince Harron17f429f2014-12-13 00:08:19 +000045import subprocess
Todd Fiala3f0a3602014-07-08 06:42:37 +000046import sys
Todd Fiala8cbeed32015-09-08 22:22:33 +000047import threading
Greg Clayton1827fc22015-09-19 00:39:09 +000048import test_results
Todd Fiala68615ce2015-09-15 21:38:04 +000049import dotest_channels
Todd Fiala8cbeed32015-09-08 22:22:33 +000050import dotest_args
Steve Puccibefe2b12014-03-07 00:01:11 +000051
Chaoren Linb6325d02015-08-12 18:02:54 +000052
Vince Harron17f429f2014-12-13 00:08:19 +000053def get_timeout_command():
Vince Harronede59652015-01-08 02:11:26 +000054 """Search for a suitable timeout command."""
Ying Chen93190c42015-07-20 20:04:22 +000055 if not sys.platform.startswith("win32"):
56 try:
57 subprocess.call("timeout", stderr=subprocess.PIPE)
58 return "timeout"
59 except OSError:
60 pass
Vince Harron17f429f2014-12-13 00:08:19 +000061 try:
Chaoren Lin45c17ff2015-05-28 23:00:10 +000062 subprocess.call("gtimeout", stderr=subprocess.PIPE)
Vince Harron17f429f2014-12-13 00:08:19 +000063 return "gtimeout"
64 except OSError:
65 pass
66 return None
67
68timeout_command = get_timeout_command()
69
Vince Harron17f429f2014-12-13 00:08:19 +000070# Status codes for running command with timeout.
71eTimedOut, ePassed, eFailed = 124, 0, 1
72
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000073output_lock = None
74test_counter = None
75total_tests = None
Chaoren Linffc63b02015-08-12 18:02:49 +000076test_name_len = None
Pavel Labath05ab2372015-07-06 15:57:52 +000077dotest_options = None
Zachary Turner38e64172015-08-10 17:46:11 +000078output_on_success = False
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
Todd Fiala1cc97b42015-09-21 05:42:26 +000087
Todd Fiala33896a92015-09-18 21:01:13 +000088def setup_global_variables(
89 lock, counter, total, name_len, options, worker_index_map):
Chaoren Linffc63b02015-08-12 18:02:49 +000090 global output_lock, test_counter, total_tests, test_name_len
91 global dotest_options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000092 output_lock = lock
93 test_counter = counter
94 total_tests = total
Chaoren Linffc63b02015-08-12 18:02:49 +000095 test_name_len = name_len
Pavel Labath05ab2372015-07-06 15:57:52 +000096 dotest_options = options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000097
Todd Fiala33896a92015-09-18 21:01:13 +000098 if worker_index_map is not None:
99 # We'll use the output lock for this to avoid sharing another lock.
100 # This won't be used much.
101 index_lock = lock
102
103 def get_worker_index_use_pid():
104 """Returns a 0-based, process-unique index for the worker."""
105 pid = os.getpid()
106 with index_lock:
107 if pid not in worker_index_map:
108 worker_index_map[pid] = len(worker_index_map)
109 return worker_index_map[pid]
110
111 global GET_WORKER_INDEX
112 GET_WORKER_INDEX = get_worker_index_use_pid
113
Chaoren Linb6325d02015-08-12 18:02:54 +0000114
Zachary Turner38e64172015-08-10 17:46:11 +0000115def report_test_failure(name, command, output):
116 global output_lock
117 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000118 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
119 print >> sys.stderr
120 print >> sys.stderr, output
121 print >> sys.stderr, "[%s FAILED]" % name
122 print >> sys.stderr, "Command invoked: %s" % ' '.join(command)
Chaoren Linffc63b02015-08-12 18:02:49 +0000123 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000124
Chaoren Linb6325d02015-08-12 18:02:54 +0000125
Zachary Turner38e64172015-08-10 17:46:11 +0000126def report_test_pass(name, output):
127 global output_lock, output_on_success
128 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000129 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
130 if output_on_success:
131 print >> sys.stderr
132 print >> sys.stderr, output
133 print >> sys.stderr, "[%s PASSED]" % name
Chaoren Linffc63b02015-08-12 18:02:49 +0000134 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000135
Chaoren Linb6325d02015-08-12 18:02:54 +0000136
Chaoren Linffc63b02015-08-12 18:02:49 +0000137def update_progress(test_name=""):
138 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000139 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000140 counter_len = len(str(total_tests))
Greg Clayton1827fc22015-09-19 00:39:09 +0000141 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
142 sys.stderr.write(
143 "\r%*d out of %d test suites processed - %-*s" %
144 (counter_len, test_counter.value, total_tests,
145 test_name_len.value, test_name))
Chaoren Linffc63b02015-08-12 18:02:49 +0000146 if len(test_name) > test_name_len.value:
147 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000148 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000149 sys.stdout.flush()
150 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000151
Chaoren Linb6325d02015-08-12 18:02:54 +0000152
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000153def parse_test_results(output):
154 passes = 0
155 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000156 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000157 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000158 pass_count = re.search("^RESULT:.*([0-9]+) passes",
159 result, re.MULTILINE)
160 fail_count = re.search("^RESULT:.*([0-9]+) failures",
161 result, re.MULTILINE)
162 error_count = re.search("^RESULT:.*([0-9]+) errors",
163 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000164 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
165 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000166 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000167 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000168 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000169 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000170 if unexpected_success_count is not None:
171 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000172 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000173 failures = failures + int(error_count.group(1))
174 pass
Zachary Turner4cceca72015-08-14 16:45:32 +0000175 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000176
Chaoren Linb6325d02015-08-12 18:02:54 +0000177
Todd Fialada817b62015-09-22 18:05:11 +0000178def create_new_process_group():
179 """Creates a new process group for the process."""
180 os.setpgid(os.getpid(), os.getpid())
181
182
Todd Fiala8cbeed32015-09-08 22:22:33 +0000183def call_with_timeout(command, timeout, name, inferior_pid_events):
Todd Fiala68615ce2015-09-15 21:38:04 +0000184 """Run command with a timeout if possible.
185 -s QUIT will create a coredump if they are enabled on your system
186 """
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000187 process = None
188 if timeout_command and timeout != "0":
189 command = [timeout_command, '-s', 'QUIT', timeout] + command
Todd Fiala68615ce2015-09-15 21:38:04 +0000190
Todd Fiala33896a92015-09-18 21:01:13 +0000191 if GET_WORKER_INDEX is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000192 try:
193 worker_index = GET_WORKER_INDEX()
194 command.extend([
Todd Fiala40b180e2015-09-18 23:46:30 +0000195 "--event-add-entries", "worker_index={}:int".format(worker_index)])
Todd Fialae83f1402015-09-18 22:45:31 +0000196 except:
197 # Ctrl-C does bad things to multiprocessing.Manager.dict() lookup.
198 pass
199
Chaoren Linb6325d02015-08-12 18:02:54 +0000200 # Specifying a value for close_fds is unsupported on Windows when using
201 # subprocess.PIPE
Zachary Turnerdc494d52015-02-07 00:14:55 +0000202 if os.name != "nt":
Chaoren Linb6325d02015-08-12 18:02:54 +0000203 process = subprocess.Popen(command,
204 stdin=subprocess.PIPE,
205 stdout=subprocess.PIPE,
206 stderr=subprocess.PIPE,
Todd Fialada817b62015-09-22 18:05:11 +0000207 close_fds=True,
208 preexec_fn=create_new_process_group)
Zachary Turnerdc494d52015-02-07 00:14:55 +0000209 else:
Chaoren Linb6325d02015-08-12 18:02:54 +0000210 process = subprocess.Popen(command,
211 stdin=subprocess.PIPE,
212 stdout=subprocess.PIPE,
213 stderr=subprocess.PIPE)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000214 inferior_pid = process.pid
215 if inferior_pid_events:
216 inferior_pid_events.put_nowait(('created', inferior_pid))
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000217 output = process.communicate()
Todd Fiala68615ce2015-09-15 21:38:04 +0000218
219 # The inferior should now be entirely wrapped up.
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000220 exit_status = process.returncode
Todd Fiala68615ce2015-09-15 21:38:04 +0000221 if exit_status is None:
222 raise Exception(
223 "no exit status available after the inferior dotest.py "
224 "should have completed")
225
Todd Fiala8cbeed32015-09-08 22:22:33 +0000226 if inferior_pid_events:
227 inferior_pid_events.put_nowait(('destroyed', inferior_pid))
228
Zachary Turner4cceca72015-08-14 16:45:32 +0000229 passes, failures, unexpected_successes = parse_test_results(output)
Zachary Turner38e64172015-08-10 17:46:11 +0000230 if exit_status == 0:
Chaoren Linb6325d02015-08-12 18:02:54 +0000231 # stdout does not have any useful information from 'dotest.py',
232 # only stderr does.
Zachary Turner38e64172015-08-10 17:46:11 +0000233 report_test_pass(name, output[1])
234 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000235 # TODO need to differentiate a failing test from a run that
236 # was broken out of by a SIGTERM/SIGKILL, reporting those as
237 # an error. If a signal-based completion, need to call that
238 # an error.
Zachary Turner38e64172015-08-10 17:46:11 +0000239 report_test_failure(name, command, output[1])
Zachary Turner4cceca72015-08-14 16:45:32 +0000240 return name, exit_status, passes, failures, unexpected_successes
Johnny Chene8d9dc62011-10-31 19:04:07 +0000241
Chaoren Linb6325d02015-08-12 18:02:54 +0000242
Todd Fiala8cbeed32015-09-08 22:22:33 +0000243def process_dir(root, files, test_root, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000244 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000245 results = []
Steve Puccibefe2b12014-03-07 00:01:11 +0000246 for name in files:
Zachary Turnerf6896b02015-01-05 19:37:03 +0000247 script_file = os.path.join(test_root, "dotest.py")
Zachary Turnerf6896b02015-01-05 19:37:03 +0000248 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000249 dotest_argv +
Todd Fialafed95662015-09-03 18:58:44 +0000250 ["--inferior", "-p", name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000251
252 timeout_name = os.path.basename(os.path.splitext(name)[0]).upper()
253
Chaoren Linb6325d02015-08-12 18:02:54 +0000254 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
255 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000256
Todd Fiala8cbeed32015-09-08 22:22:33 +0000257 results.append(call_with_timeout(
258 command, timeout, name, inferior_pid_events))
Vince Harron17f429f2014-12-13 00:08:19 +0000259
Zachary Turner4cceca72015-08-14 16:45:32 +0000260 # result = (name, status, passes, failures, unexpected_successes)
261 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000262 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000263 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000264 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000265 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000266 if status != ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000267 unexpected_passes = [name for name, _, _, _, unexpected_successes in results
268 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000269
Chaoren Line80372a2015-08-12 18:02:53 +0000270 pass_count = sum([result[2] for result in results])
271 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000272
Zachary Turner4cceca72015-08-14 16:45:32 +0000273 return (timed_out, passed, failed, unexpected_passes, pass_count, fail_count)
Steve Puccibefe2b12014-03-07 00:01:11 +0000274
275in_q = None
276out_q = None
277
Chaoren Linb6325d02015-08-12 18:02:54 +0000278
Todd Fiala8cbeed32015-09-08 22:22:33 +0000279def process_dir_worker_multiprocessing(
280 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000281 a_dotest_options, job_queue, result_queue, inferior_pid_events,
282 worker_index_map):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000283 """Worker thread main loop when in multiprocessing mode.
Steve Puccibefe2b12014-03-07 00:01:11 +0000284 Takes one directory specification at a time and works on it."""
Todd Fiala8cbeed32015-09-08 22:22:33 +0000285
286 # Shut off interrupt handling in the child process.
287 signal.signal(signal.SIGINT, signal.SIG_IGN)
Todd Fiala1cc97b42015-09-21 05:42:26 +0000288 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000289
290 # Setup the global state for the worker process.
291 setup_global_variables(
292 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000293 a_dotest_options, worker_index_map)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000294
295 # Keep grabbing entries from the queue until done.
296 while not job_queue.empty():
297 try:
298 job = job_queue.get(block=False)
299 result = process_dir(job[0], job[1], job[2], job[3],
300 inferior_pid_events)
301 result_queue.put(result)
302 except Queue.Empty:
303 # Fine, we're done.
304 pass
Steve Puccibefe2b12014-03-07 00:01:11 +0000305
Chaoren Linb6325d02015-08-12 18:02:54 +0000306
Todd Fiala8cbeed32015-09-08 22:22:33 +0000307def process_dir_worker_multiprocessing_pool(args):
308 return process_dir(*args)
309
310
Todd Fiala68615ce2015-09-15 21:38:04 +0000311def process_dir_worker_threading(job_queue, result_queue, inferior_pid_events):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000312 """Worker thread main loop when in threading mode.
313
314 This one supports the hand-rolled pooling support.
315
316 Takes one directory specification at a time and works on it."""
317
318 # Keep grabbing entries from the queue until done.
319 while not job_queue.empty():
320 try:
321 job = job_queue.get(block=False)
322 result = process_dir(job[0], job[1], job[2], job[3],
323 inferior_pid_events)
324 result_queue.put(result)
325 except Queue.Empty:
326 # Fine, we're done.
327 pass
328
329
330def process_dir_worker_threading_pool(args):
331 return process_dir(*args)
332
333
334def process_dir_mapper_inprocess(args):
335 """Map adapter for running the subprocess-based, non-threaded test runner.
336
337 @param args the process work item tuple
338 @return the test result tuple
339 """
340 return process_dir(*args)
341
342
343def collect_active_pids_from_pid_events(event_queue):
344 """
345 Returns the set of what should be active inferior pids based on
346 the event stream.
347
348 @param event_queue a multiprocessing.Queue containing events of the
349 form:
350 ('created', pid)
351 ('destroyed', pid)
352
353 @return set of inferior dotest.py pids activated but never completed.
354 """
355 active_pid_set = set()
356 while not event_queue.empty():
357 pid_event = event_queue.get_nowait()
358 if pid_event[0] == 'created':
359 active_pid_set.add(pid_event[1])
360 elif pid_event[0] == 'destroyed':
361 active_pid_set.remove(pid_event[1])
362 return active_pid_set
363
364
365def kill_all_worker_processes(workers, inferior_pid_events):
366 """
367 Kills all specified worker processes and their process tree.
368
369 @param workers a list of multiprocess.Process worker objects.
370 @param inferior_pid_events a multiprocess.Queue that contains
371 all inferior create and destroy events. Used to construct
372 the list of child pids still outstanding that need to be killed.
373 """
374 for worker in workers:
375 worker.terminate()
376 worker.join()
377
378 # Add all the child test pids created.
379 active_pid_set = collect_active_pids_from_pid_events(
380 inferior_pid_events)
381 for inferior_pid in active_pid_set:
382 print "killing inferior pid {}".format(inferior_pid)
383 os.kill(inferior_pid, signal.SIGKILL)
384
385
386def kill_all_worker_threads(workers, inferior_pid_events):
387 """
388 Kills all specified worker threads and their process tree.
389
390 @param workers a list of multiprocess.Process worker objects.
391 @param inferior_pid_events a multiprocess.Queue that contains
392 all inferior create and destroy events. Used to construct
393 the list of child pids still outstanding that need to be killed.
394 """
395
396 # Add all the child test pids created.
397 active_pid_set = collect_active_pids_from_pid_events(
398 inferior_pid_events)
399 for inferior_pid in active_pid_set:
400 print "killing inferior pid {}".format(inferior_pid)
401 os.kill(inferior_pid, signal.SIGKILL)
402
403 # We don't have a way to nuke the threads. However, since we killed
404 # all the inferiors, and we drained the job queue, this will be
405 # good enough. Wait cleanly for each worker thread to wrap up.
406 for worker in workers:
407 worker.join()
408
409
410def find_test_files_in_dir_tree(dir_root, found_func):
411 """Calls found_func for all the test files in the given dir hierarchy.
412
413 @param dir_root the path to the directory to start scanning
414 for test files. All files in this directory and all its children
415 directory trees will be searched.
416
417 @param found_func a callable object that will be passed
418 the parent directory (relative to dir_root) and the list of
419 test files from within that directory.
420 """
421 for root, _, files in os.walk(dir_root, topdown=False):
422 def is_test_filename(test_dir, base_filename):
423 """Returns True if the given filename matches the test name format.
424
425 @param test_dir the directory to check. Should be absolute or
426 relative to current working directory.
427
428 @param base_filename the base name of the filename to check for a
429 dherence to the python test case filename format.
430
431 @return True if name matches the python test case filename format.
432 """
433 # Not interested in symbolically linked files.
434 if os.path.islink(os.path.join(test_dir, base_filename)):
435 return False
436 # Only interested in test files with the "Test*.py" naming pattern.
437 return (base_filename.startswith("Test") and
438 base_filename.endswith(".py"))
439
440 tests = [filename for filename in files
441 if is_test_filename(root, filename)]
442 if tests:
443 found_func(root, tests)
444
445
446def initialize_global_vars_common(num_threads, test_work_items):
447 global total_tests, test_counter, test_name_len
Greg Clayton1827fc22015-09-19 00:39:09 +0000448
Todd Fiala8cbeed32015-09-08 22:22:33 +0000449 total_tests = sum([len(item[1]) for item in test_work_items])
450 test_counter = multiprocessing.Value('i', 0)
451 test_name_len = multiprocessing.Value('i', 0)
Greg Clayton1827fc22015-09-19 00:39:09 +0000452 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
453 print >> sys.stderr, "Testing: %d test suites, %d thread%s" % (
454 total_tests, num_threads, (num_threads > 1) * "s")
Todd Fiala8cbeed32015-09-08 22:22:33 +0000455 update_progress()
456
457
458def initialize_global_vars_multiprocessing(num_threads, test_work_items):
459 # Initialize the global state we'll use to communicate with the
460 # rest of the flat module.
461 global output_lock
462 output_lock = multiprocessing.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000463
Todd Fiala8cbeed32015-09-08 22:22:33 +0000464 initialize_global_vars_common(num_threads, test_work_items)
465
466
467def initialize_global_vars_threading(num_threads, test_work_items):
Todd Fiala33896a92015-09-18 21:01:13 +0000468 """Initializes global variables used in threading mode.
469 @param num_threads specifies the number of workers used.
470 @param test_work_items specifies all the work items
471 that will be processed.
472 """
Todd Fiala8cbeed32015-09-08 22:22:33 +0000473 # Initialize the global state we'll use to communicate with the
474 # rest of the flat module.
475 global output_lock
476 output_lock = threading.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000477
478 index_lock = threading.RLock()
479 index_map = {}
480
481 def get_worker_index_threading():
482 """Returns a 0-based, thread-unique index for the worker thread."""
483 thread_id = threading.current_thread().ident
484 with index_lock:
485 if thread_id not in index_map:
486 index_map[thread_id] = len(index_map)
487 return index_map[thread_id]
488
489
490 global GET_WORKER_INDEX
491 GET_WORKER_INDEX = get_worker_index_threading
492
Todd Fiala8cbeed32015-09-08 22:22:33 +0000493 initialize_global_vars_common(num_threads, test_work_items)
494
495
Todd Fiala68615ce2015-09-15 21:38:04 +0000496def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler):
497 """Provides a main loop that is Ctrl-C protected.
498
499 The main loop calls the main_op_func() repeatedly until done_func()
500 returns true. The ctrl_c_handler() method is called with a single
501 int parameter that contains the number of times the ctrl_c has been
502 hit (starting with 1). The ctrl_c_handler() should mutate whatever
503 it needs to have the done_func() return True as soon as it is desired
504 to exit the loop.
505 """
506 done = False
507 ctrl_c_count = 0
508
509 while not done:
510 try:
511 # See if we're done. Start with done check since it is
512 # the first thing executed after a Ctrl-C handler in the
513 # following loop.
514 done = done_func()
515 if not done:
516 # Run the main op once.
517 main_op_func()
518
519 except KeyboardInterrupt:
520 ctrl_c_count += 1
521 ctrl_c_handler(ctrl_c_count)
522
523
524def pump_workers_and_asyncore_map(workers, asyncore_map):
525 """Prunes out completed workers and maintains the asyncore loop.
526
527 The asyncore loop contains the optional socket listener
528 and handlers. When all workers are complete, this method
529 takes care of stopping the listener. It also runs the
530 asyncore loop for the given async map for 10 iterations.
531
532 @param workers the list of worker Thread/Process instances.
533
534 @param asyncore_map the asyncore threading-aware map that
535 indicates which channels are in use and still alive.
536 """
537
538 # Check on all the workers, removing them from the workers
539 # list as they complete.
540 dead_workers = []
541 for worker in workers:
542 # This non-blocking join call is what allows us
543 # to still receive keyboard interrupts.
544 worker.join(0.01)
545 if not worker.is_alive():
546 dead_workers.append(worker)
547 # Clear out the completed workers
548 for dead_worker in dead_workers:
549 workers.remove(dead_worker)
550
551 # If there are no more workers and there is a listener,
552 # close the listener.
553 global RESULTS_LISTENER_CHANNEL
554 if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None:
555 RESULTS_LISTENER_CHANNEL.close()
556 RESULTS_LISTENER_CHANNEL = None
557
558 # Pump the asyncore map if it isn't empty.
559 if len(asyncore_map) > 0:
560 asyncore.loop(0.1, False, asyncore_map, 10)
561
562
563def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events,
564 stop_all_inferiors_func):
565 """Performs the appropriate ctrl-c action for non-pool parallel test runners
566
567 @param ctrl_c_count starting with 1, indicates the number of times ctrl-c
568 has been intercepted. The value is 1 on the first intercept, 2 on the
569 second, etc.
570
571 @param job_queue a Queue object that contains the work still outstanding
572 (i.e. hasn't been assigned to a worker yet).
573
574 @param workers list of Thread or Process workers.
575
576 @param inferior_pid_events specifies a Queue of inferior process
577 construction and destruction events. Used to build the list of inferior
578 processes that should be killed if we get that far.
579
580 @param stop_all_inferiors_func a callable object that takes the
581 workers and inferior_pid_events parameters (in that order) if a hard
582 stop is to be used on the workers.
583 """
584
585 # Print out which Ctrl-C we're handling.
586 key_name = [
587 "first",
588 "second",
589 "third",
590 "many"]
591
592 if ctrl_c_count < len(key_name):
593 name_index = ctrl_c_count - 1
594 else:
595 name_index = len(key_name) - 1
596 message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index])
597 with output_lock:
598 print message
599
600 if ctrl_c_count == 1:
601 # Remove all outstanding items from the work queue so we stop
602 # doing any more new work.
603 while not job_queue.empty():
604 try:
605 # Just drain it to stop more work from being started.
606 job_queue.get_nowait()
607 except Queue.Empty:
608 pass
609 with output_lock:
610 print "Stopped more work from being started."
611 elif ctrl_c_count == 2:
612 # Try to stop all inferiors, even the ones currently doing work.
613 stop_all_inferiors_func(workers, inferior_pid_events)
614 else:
615 with output_lock:
616 print "All teardown activities kicked off, should finish soon."
617
618
619def workers_and_async_done(workers, async_map):
620 """Returns True if the workers list and asyncore channels are all done.
621
622 @param workers list of workers (threads/processes). These must adhere
623 to the threading Thread or multiprocessing.Process interface.
624
625 @param async_map the threading-aware asyncore channel map to check
626 for live channels.
627
628 @return False if the workers list exists and has any entries in it, or
629 if the async_map exists and has any entries left in it; otherwise, True.
630 """
631 if workers is not None and len(workers) > 0:
632 # We're not done if we still have workers left.
633 return False
634 if async_map is not None and len(async_map) > 0:
635 return False
636 # We're done.
637 return True
638
639
Todd Fiala8cbeed32015-09-08 22:22:33 +0000640def multiprocessing_test_runner(num_threads, test_work_items):
641 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
642
643 This concurrent test runner is based on the multiprocessing
644 library, and rolls its own worker pooling strategy so it
645 can handle Ctrl-C properly.
646
647 This test runner is known to have an issue running on
648 Windows platforms.
649
650 @param num_threads the number of worker processes to use.
651
652 @param test_work_items the iterable of test work item tuples
653 to run.
654 """
655
656 # Initialize our global state.
657 initialize_global_vars_multiprocessing(num_threads, test_work_items)
658
659 # Create jobs.
660 job_queue = multiprocessing.Queue(len(test_work_items))
661 for test_work_item in test_work_items:
662 job_queue.put(test_work_item)
663
664 result_queue = multiprocessing.Queue(len(test_work_items))
665
666 # Create queues for started child pids. Terminating
667 # the multiprocess processes does not terminate the
668 # child processes they spawn. We can remove this tracking
669 # if/when we move to having the multiprocess process directly
670 # perform the test logic. The Queue size needs to be able to
671 # hold 2 * (num inferior dotest.py processes started) entries.
672 inferior_pid_events = multiprocessing.Queue(4096)
673
Todd Fiala33896a92015-09-18 21:01:13 +0000674 # Worker dictionary allows each worker to figure out its worker index.
675 manager = multiprocessing.Manager()
676 worker_index_map = manager.dict()
677
Todd Fiala8cbeed32015-09-08 22:22:33 +0000678 # Create workers. We don't use multiprocessing.Pool due to
679 # challenges with handling ^C keyboard interrupts.
680 workers = []
681 for _ in range(num_threads):
682 worker = multiprocessing.Process(
683 target=process_dir_worker_multiprocessing,
684 args=(output_lock,
685 test_counter,
686 total_tests,
687 test_name_len,
688 dotest_options,
689 job_queue,
690 result_queue,
Todd Fiala33896a92015-09-18 21:01:13 +0000691 inferior_pid_events,
692 worker_index_map))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000693 worker.start()
694 workers.append(worker)
695
Todd Fiala68615ce2015-09-15 21:38:04 +0000696 # Main loop: wait for all workers to finish and wait for
697 # the socket handlers to wrap up.
698 ctrl_c_loop(
699 # Main operation of loop
700 lambda: pump_workers_and_asyncore_map(
701 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000702
Todd Fiala68615ce2015-09-15 21:38:04 +0000703 # Return True when we're done with the main loop.
704 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000705
Todd Fiala68615ce2015-09-15 21:38:04 +0000706 # Indicate what we do when we receive one or more Ctrl-Cs.
707 lambda ctrl_c_count: handle_ctrl_c(
708 ctrl_c_count, job_queue, workers, inferior_pid_events,
709 kill_all_worker_processes))
710
711 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000712 test_results = []
713 while not result_queue.empty():
714 test_results.append(result_queue.get(block=False))
715 return test_results
716
717
Todd Fiala68615ce2015-09-15 21:38:04 +0000718def map_async_run_loop(future, channel_map, listener_channel):
719 """Blocks until the Pool.map_async completes and the channel completes.
720
721 @param future an AsyncResult instance from a Pool.map_async() call.
722
723 @param channel_map the asyncore dispatch channel map that should be pumped.
724 Optional: may be None.
725
726 @param listener_channel the channel representing a listener that should be
727 closed once the map_async results are available.
728
729 @return the results from the async_result instance.
730 """
731 map_results = None
732
733 done = False
734 while not done:
735 # Check if we need to reap the map results.
736 if map_results is None:
737 if future.ready():
738 # Get the results.
739 map_results = future.get()
740
741 # Close the runner process listener channel if we have
742 # one: no more connections will be incoming.
743 if listener_channel is not None:
744 listener_channel.close()
745
746 # Pump the asyncore loop if we have a listener socket.
747 if channel_map is not None:
748 asyncore.loop(0.01, False, channel_map, 10)
749
750 # Figure out if we're done running.
751 done = map_results is not None
752 if channel_map is not None:
753 # We have a runner process async map. Check if it
754 # is complete.
755 if len(channel_map) > 0:
756 # We still have an asyncore channel running. Not done yet.
757 done = False
758
759 return map_results
760
761
Todd Fiala8cbeed32015-09-08 22:22:33 +0000762def multiprocessing_test_runner_pool(num_threads, test_work_items):
763 # Initialize our global state.
764 initialize_global_vars_multiprocessing(num_threads, test_work_items)
765
Todd Fiala33896a92015-09-18 21:01:13 +0000766 manager = multiprocessing.Manager()
767 worker_index_map = manager.dict()
768
Todd Fiala8cbeed32015-09-08 22:22:33 +0000769 pool = multiprocessing.Pool(
770 num_threads,
771 initializer=setup_global_variables,
772 initargs=(output_lock, test_counter, total_tests, test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000773 dotest_options, worker_index_map))
Todd Fiala68615ce2015-09-15 21:38:04 +0000774
775 # Start the map operation (async mode).
776 map_future = pool.map_async(
777 process_dir_worker_multiprocessing_pool, test_work_items)
778 return map_async_run_loop(
779 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000780
781
782def threading_test_runner(num_threads, test_work_items):
783 """Provides hand-wrapped pooling threading-based test runner adapter
784 with Ctrl-C support.
785
786 This concurrent test runner is based on the threading
787 library, and rolls its own worker pooling strategy so it
788 can handle Ctrl-C properly.
789
790 @param num_threads the number of worker processes to use.
791
792 @param test_work_items the iterable of test work item tuples
793 to run.
794 """
795
796 # Initialize our global state.
797 initialize_global_vars_threading(num_threads, test_work_items)
798
799 # Create jobs.
800 job_queue = Queue.Queue()
801 for test_work_item in test_work_items:
802 job_queue.put(test_work_item)
803
804 result_queue = Queue.Queue()
805
806 # Create queues for started child pids. Terminating
807 # the threading threads does not terminate the
808 # child processes they spawn.
809 inferior_pid_events = Queue.Queue()
810
811 # Create workers. We don't use multiprocessing.pool.ThreadedPool
812 # due to challenges with handling ^C keyboard interrupts.
813 workers = []
814 for _ in range(num_threads):
815 worker = threading.Thread(
816 target=process_dir_worker_threading,
Todd Fiala68615ce2015-09-15 21:38:04 +0000817 args=(job_queue,
Todd Fiala8cbeed32015-09-08 22:22:33 +0000818 result_queue,
819 inferior_pid_events))
820 worker.start()
821 workers.append(worker)
822
Todd Fiala68615ce2015-09-15 21:38:04 +0000823 # Main loop: wait for all workers to finish and wait for
824 # the socket handlers to wrap up.
825 ctrl_c_loop(
826 # Main operation of loop
827 lambda: pump_workers_and_asyncore_map(
828 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000829
Todd Fiala68615ce2015-09-15 21:38:04 +0000830 # Return True when we're done with the main loop.
831 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000832
Todd Fiala68615ce2015-09-15 21:38:04 +0000833 # Indicate what we do when we receive one or more Ctrl-Cs.
834 lambda ctrl_c_count: handle_ctrl_c(
835 ctrl_c_count, job_queue, workers, inferior_pid_events,
836 kill_all_worker_threads))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000837
Todd Fiala68615ce2015-09-15 21:38:04 +0000838 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000839 test_results = []
840 while not result_queue.empty():
841 test_results.append(result_queue.get(block=False))
842 return test_results
843
844
845def threading_test_runner_pool(num_threads, test_work_items):
846 # Initialize our global state.
847 initialize_global_vars_threading(num_threads, test_work_items)
848
Todd Fiala68615ce2015-09-15 21:38:04 +0000849 pool = multiprocessing.pool.ThreadPool(num_threads)
850 map_future = pool.map_async(
851 process_dir_worker_threading_pool, test_work_items)
852
853 return map_async_run_loop(
854 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
855
856
857def asyncore_run_loop(channel_map):
858 try:
859 asyncore.loop(None, False, channel_map)
860 except:
861 # Swallow it, we're seeing:
862 # error: (9, 'Bad file descriptor')
863 # when the listener channel is closed. Shouldn't be the case.
864 pass
Todd Fiala8cbeed32015-09-08 22:22:33 +0000865
866
867def inprocess_exec_test_runner(test_work_items):
868 # Initialize our global state.
869 initialize_global_vars_multiprocessing(1, test_work_items)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000870
Todd Fiala33896a92015-09-18 21:01:13 +0000871 # We're always worker index 0
872 global GET_WORKER_INDEX
873 GET_WORKER_INDEX = lambda: 0
874
Todd Fiala68615ce2015-09-15 21:38:04 +0000875 # Run the listener and related channel maps in a separate thread.
876 # global RUNNER_PROCESS_ASYNC_MAP
877 global RESULTS_LISTENER_CHANNEL
878 if RESULTS_LISTENER_CHANNEL is not None:
879 socket_thread = threading.Thread(
880 target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP))
881 socket_thread.start()
882
883 # Do the work.
884 test_results = map(process_dir_mapper_inprocess, test_work_items)
885
886 # If we have a listener channel, shut it down here.
887 if RESULTS_LISTENER_CHANNEL is not None:
888 # Close down the channel.
889 RESULTS_LISTENER_CHANNEL.close()
890 RESULTS_LISTENER_CHANNEL = None
891
892 # Wait for the listener and handlers to complete.
893 socket_thread.join()
894
895 return test_results
Todd Fiala8cbeed32015-09-08 22:22:33 +0000896
897def walk_and_invoke(test_directory, test_subdir, dotest_argv,
Todd Fiala871b2e52015-09-22 22:47:34 +0000898 num_workers, test_runner_func):
Steve Puccibefe2b12014-03-07 00:01:11 +0000899 """Look for matched files and invoke test driver on each one.
900 In single-threaded mode, each test driver is invoked directly.
901 In multi-threaded mode, submit each test driver to a worker
Vince Harrone06a7a82015-05-12 23:12:19 +0000902 queue, and then wait for all to complete.
903
904 test_directory - lldb/test/ directory
Chaoren Linb6325d02015-08-12 18:02:54 +0000905 test_subdir - lldb/test/ or a subfolder with the tests we're interested in
906 running
Vince Harrone06a7a82015-05-12 23:12:19 +0000907 """
Todd Fiala68615ce2015-09-15 21:38:04 +0000908 # The async_map is important to keep all thread-related asyncore
909 # channels distinct when we call asyncore.loop() later on.
910 global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP
911 RUNNER_PROCESS_ASYNC_MAP = {}
912
913 # If we're outputting side-channel test results, create the socket
914 # listener channel and tell the inferior to send results to the
915 # port on which we'll be listening.
916 if RESULTS_FORMATTER is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000917 forwarding_func = RESULTS_FORMATTER.handle_event
Todd Fiala68615ce2015-09-15 21:38:04 +0000918 RESULTS_LISTENER_CHANNEL = (
919 dotest_channels.UnpicklingForwardingListenerChannel(
Todd Fiala871b2e52015-09-22 22:47:34 +0000920 RUNNER_PROCESS_ASYNC_MAP, "localhost", 0,
921 2 * num_workers, forwarding_func))
Todd Fiala68615ce2015-09-15 21:38:04 +0000922 dotest_argv.append("--results-port")
923 dotest_argv.append(str(RESULTS_LISTENER_CHANNEL.address[1]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000924
925 # Collect the test files that we'll run.
926 test_work_items = []
Todd Fiala8cbeed32015-09-08 22:22:33 +0000927 find_test_files_in_dir_tree(
928 test_subdir, lambda testdir, test_files: test_work_items.append([
929 test_subdir, test_files, test_directory, dotest_argv, None]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000930
Todd Fiala8cbeed32015-09-08 22:22:33 +0000931 # Convert test work items into test results using whatever
932 # was provided as the test run function.
933 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +0000934
Todd Fiala8cbeed32015-09-08 22:22:33 +0000935 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +0000936 timed_out = sum([result[0] for result in test_results], [])
937 passed = sum([result[1] for result in test_results], [])
938 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +0000939 unexpected_successes = sum([result[3] for result in test_results], [])
940 pass_count = sum([result[4] for result in test_results])
941 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +0000942
Todd Fiala8cbeed32015-09-08 22:22:33 +0000943 return (timed_out, passed, failed, unexpected_successes, pass_count,
944 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +0000945
Chaoren Linb6325d02015-08-12 18:02:54 +0000946
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000947def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +0000948 # returns a set of test filenames that might timeout
949 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +0000950 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000951 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +0000952 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000953 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000954 m = re.search(r'remote-(\w+)', platform_name)
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000955 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +0000956
957 expected_timeout = set()
958
959 if target.startswith("linux"):
960 expected_timeout |= {
Chaoren Lin0b8bb3d2015-07-22 20:52:17 +0000961 "TestProcessAttach.py",
Vince Harron06381732015-05-12 23:10:36 +0000962 "TestConnectRemote.py",
963 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +0000964 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +0000965 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +0000966
967 # Times out in ~10% of the times on the build bot
968 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +0000969 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +0000970 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +0000971 "TestThreadStepOut.py",
972 }
973 elif target.startswith("android"):
974 expected_timeout |= {
975 "TestExitDuringStep.py",
976 "TestHelloWorld.py",
977 }
Chaoren Linfebef1b2015-08-19 17:22:12 +0000978 if host.startswith("win32"):
979 expected_timeout |= {
980 "TestEvents.py",
981 "TestThreadStates.py",
982 }
Ed Maste4dd8fba2015-05-14 16:25:52 +0000983 elif target.startswith("freebsd"):
984 expected_timeout |= {
985 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +0000986 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +0000987 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +0000988 "TestWatchpointConditionAPI.py",
989 }
Vince Harron0f173ac2015-05-18 19:36:33 +0000990 elif target.startswith("darwin"):
991 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +0000992 # times out on MBP Retina, Mid 2012
993 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +0000994 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +0000995 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +0000996 }
Vince Harron06381732015-05-12 23:10:36 +0000997 return expected_timeout
998
Chaoren Linb6325d02015-08-12 18:02:54 +0000999
Pavel Labathfad30cf2015-06-29 14:16:51 +00001000def getDefaultTimeout(platform_name):
1001 if os.getenv("LLDB_TEST_TIMEOUT"):
1002 return os.getenv("LLDB_TEST_TIMEOUT")
1003
1004 if platform_name is None:
1005 platform_name = sys.platform
1006
1007 if platform_name.startswith("remote-"):
1008 return "10m"
1009 else:
1010 return "4m"
1011
Chaoren Linb6325d02015-08-12 18:02:54 +00001012
Vince Harron0b9dbb52015-05-21 18:18:52 +00001013def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +00001014 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +00001015 os.utime(fname, times)
1016
Chaoren Linb6325d02015-08-12 18:02:54 +00001017
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001018def find(pattern, path):
1019 result = []
1020 for root, dirs, files in os.walk(path):
1021 for name in files:
1022 if fnmatch.fnmatch(name, pattern):
1023 result.append(os.path.join(root, name))
1024 return result
1025
Chaoren Linb6325d02015-08-12 18:02:54 +00001026
Todd Fiala8cbeed32015-09-08 22:22:33 +00001027def get_test_runner_strategies(num_threads):
1028 """Returns the test runner strategies by name in a dictionary.
1029
1030 @param num_threads specifies the number of threads/processes
1031 that will be used for concurrent test runners.
1032
1033 @return dictionary with key as test runner strategy name and
1034 value set to a callable object that takes the test work item
1035 and returns a test result tuple.
1036 """
1037 return {
1038 # multiprocessing supports ctrl-c and does not use
1039 # multiprocessing.Pool.
1040 "multiprocessing":
1041 (lambda work_items: multiprocessing_test_runner(
1042 num_threads, work_items)),
1043
1044 # multiprocessing-pool uses multiprocessing.Pool but
1045 # does not support Ctrl-C.
1046 "multiprocessing-pool":
1047 (lambda work_items: multiprocessing_test_runner_pool(
1048 num_threads, work_items)),
1049
1050 # threading uses a hand-rolled worker pool much
1051 # like multiprocessing, but instead uses in-process
1052 # worker threads. This one supports Ctrl-C.
1053 "threading":
1054 (lambda work_items: threading_test_runner(num_threads, work_items)),
1055
1056 # threading-pool uses threading for the workers (in-process)
1057 # and uses the multiprocessing.pool thread-enabled pool.
Todd Fiala68615ce2015-09-15 21:38:04 +00001058 # This does not properly support Ctrl-C.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001059 "threading-pool":
1060 (lambda work_items: threading_test_runner_pool(
1061 num_threads, work_items)),
1062
1063 # serial uses the subprocess-based, single process
1064 # test runner. This provides process isolation but
Todd Fiala68615ce2015-09-15 21:38:04 +00001065 # no concurrent test execution.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001066 "serial":
1067 inprocess_exec_test_runner
1068 }
1069
1070
Todd Fialaea736242015-09-23 15:21:28 +00001071def _remove_option(
1072 args, long_option_name, short_option_name, takes_arg):
Todd Fiala68615ce2015-09-15 21:38:04 +00001073 """Removes option and related option arguments from args array.
Todd Fialaea736242015-09-23 15:21:28 +00001074
1075 This method removes all short/long options that match the given
1076 arguments.
1077
Todd Fiala68615ce2015-09-15 21:38:04 +00001078 @param args the array of command line arguments (in/out)
Todd Fialaea736242015-09-23 15:21:28 +00001079
1080 @param long_option_name the full command line representation of the
1081 long-form option that will be removed (including '--').
1082
1083 @param short_option_name the short version of the command line option
1084 that will be removed (including '-').
1085
1086 @param takes_arg True if the option takes an argument.
1087
Todd Fiala68615ce2015-09-15 21:38:04 +00001088 """
Todd Fialaea736242015-09-23 15:21:28 +00001089 if long_option_name is not None:
1090 regex_string = "^" + long_option_name + "="
1091 long_regex = re.compile(regex_string)
1092 if short_option_name is not None:
1093 # Short options we only match the -X and assume
1094 # any arg is one command line argument jammed together.
1095 # i.e. -O--abc=1 is a single argument in the args list.
1096 # We don't handle -O --abc=1, as argparse doesn't handle
1097 # it, either.
1098 regex_string = "^" + short_option_name
1099 short_regex = re.compile(regex_string)
1100
1101 def remove_long_internal():
1102 """Removes one matching long option from args.
1103 @returns True if one was found and removed; False otherwise.
1104 """
1105 try:
1106 index = args.index(long_option_name)
1107 # Handle the exact match case.
1108 if takes_arg:
1109 removal_count = 2
1110 else:
1111 removal_count = 1
1112 del args[index:index+removal_count]
1113 return True
1114 except ValueError:
1115 # Thanks to argparse not handling options with known arguments
1116 # like other options parsing libraries (see
1117 # https://bugs.python.org/issue9334), we need to support the
1118 # --results-formatter-options={second-level-arguments} (note
1119 # the equal sign to fool the first-level arguments parser into
1120 # not treating the second-level arguments as first-level
1121 # options). We're certainly at risk of getting this wrong
1122 # since now we're forced into the business of trying to figure
1123 # out what is an argument (although I think this
1124 # implementation will suffice).
1125 for index in range(len(args)):
1126 match = long_regex.search(args[index])
1127 if match:
1128 del args[index]
1129 return True
1130 return False
1131
1132 def remove_short_internal():
1133 """Removes one matching short option from args.
1134 @returns True if one was found and removed; False otherwise.
1135 """
Todd Fiala68615ce2015-09-15 21:38:04 +00001136 for index in range(len(args)):
Todd Fialaea736242015-09-23 15:21:28 +00001137 match = short_regex.search(args[index])
Todd Fiala68615ce2015-09-15 21:38:04 +00001138 if match:
Todd Fiala68615ce2015-09-15 21:38:04 +00001139 del args[index]
Todd Fialaea736242015-09-23 15:21:28 +00001140 return True
1141 return False
Todd Fiala68615ce2015-09-15 21:38:04 +00001142
Todd Fialaea736242015-09-23 15:21:28 +00001143 removal_count = 0
1144 while long_option_name is not None and remove_long_internal():
1145 removal_count += 1
1146 while short_option_name is not None and remove_short_internal():
1147 removal_count += 1
1148 if removal_count == 0:
1149 raise Exception(
1150 "failed to find at least one of '{}', '{}' in options".format(
1151 long_option_name, short_option_name))
Todd Fiala68615ce2015-09-15 21:38:04 +00001152
1153
1154def adjust_inferior_options(dotest_argv):
1155 """Adjusts the commandline args array for inferiors.
1156
1157 This method adjusts the inferior dotest commandline options based
1158 on the parallel test runner's options. Some of the inferior options
1159 will need to change to properly handle aggregation functionality.
1160 """
1161 global dotest_options
1162
1163 # If we don't have a session directory, create one.
1164 if not dotest_options.s:
1165 # no session log directory, we need to add this to prevent
1166 # every dotest invocation from creating its own directory
1167 import datetime
1168 # The windows platforms don't like ':' in the pathname.
1169 timestamp_started = datetime.datetime.now().strftime("%F-%H_%M_%S")
1170 dotest_argv.append('-s')
1171 dotest_argv.append(timestamp_started)
1172 dotest_options.s = timestamp_started
1173
1174 # Adjust inferior results formatter options - if the parallel
1175 # test runner is collecting into the user-specified test results,
1176 # we'll have inferiors spawn with the --results-port option and
1177 # strip the original test runner options.
1178 if dotest_options.results_file is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001179 _remove_option(dotest_argv, "--results-file", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001180 if dotest_options.results_port is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001181 _remove_option(dotest_argv, "--results-port", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001182 if dotest_options.results_formatter is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001183 _remove_option(dotest_argv, "--results-formatter", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001184 if dotest_options.results_formatter_options is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001185 _remove_option(dotest_argv, "--results-formatter-option", "-O",
1186 True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001187
Todd Fiala33896a92015-09-18 21:01:13 +00001188 # Remove test runner name if present.
1189 if dotest_options.test_runner_name is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001190 _remove_option(dotest_argv, "--test-runner-name", None, True)
Todd Fiala33896a92015-09-18 21:01:13 +00001191
1192
Todd Fiala83c32e32015-09-22 21:19:40 +00001193def is_darwin_version_lower_than(target_version):
1194 """Checks that os is Darwin and version is lower than target_version.
1195
1196 @param target_version the StrictVersion indicating the version
1197 we're checking against.
1198
1199 @return True if the OS is Darwin (OS X) and the version number of
1200 the OS is less than target_version; False in all other cases.
1201 """
1202 if platform.system() != 'Darwin':
1203 # Can't be Darwin lower than a certain version.
1204 return False
1205
1206 system_version = distutils.version.StrictVersion(platform.mac_ver()[0])
Todd Fiala871b2e52015-09-22 22:47:34 +00001207 return cmp(system_version, target_version) < 0
Todd Fiala83c32e32015-09-22 21:19:40 +00001208
1209
1210def default_test_runner_name(num_threads):
1211 """Returns the default test runner name for the configuration.
1212
1213 @param num_threads the number of threads/workers this test runner is
1214 supposed to use.
1215
1216 @return the test runner name that should be used by default when
1217 no test runner was explicitly called out on the command line.
1218 """
1219 if num_threads == 1:
1220 # Use the serial runner.
1221 test_runner_name = "serial"
1222 elif os.name == "nt":
1223 # Currently the multiprocessing test runner with ctrl-c
1224 # support isn't running correctly on nt. Use the pool
1225 # support without ctrl-c.
1226 test_runner_name = "threading-pool"
1227 elif is_darwin_version_lower_than(
1228 distutils.version.StrictVersion("10.10.0")):
1229 # OS X versions before 10.10 appear to have an issue using
1230 # the threading test runner. Fall back to multiprocessing.
1231 # Supports Ctrl-C.
1232 test_runner_name = "multiprocessing"
1233 else:
1234 # For everyone else, use the ctrl-c-enabled threading support.
1235 # Should use fewer system resources than the multprocessing
1236 # variant.
1237 test_runner_name = "threading"
1238 return test_runner_name
1239
1240
Todd Fiala8cbeed32015-09-08 22:22:33 +00001241def main(print_details_on_success, num_threads, test_subdir,
Todd Fiala68615ce2015-09-15 21:38:04 +00001242 test_runner_name, results_formatter):
Todd Fialafed95662015-09-03 18:58:44 +00001243 """Run dotest.py in inferior mode in parallel.
1244
1245 @param print_details_on_success the parsed value of the output-on-success
1246 command line argument. When True, details of a successful dotest inferior
1247 are printed even when everything succeeds. The normal behavior is to
1248 not print any details when all the inferior tests pass.
1249
1250 @param num_threads the parsed value of the num-threads command line
1251 argument.
1252
1253 @param test_subdir optionally specifies a subdir to limit testing
1254 within. May be None if the entire test tree is to be used. This subdir
1255 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001256
1257 @param test_runner_name if specified, contains the test runner
1258 name which selects the strategy used to run the isolated and
1259 optionally concurrent test runner. Specify None to allow the
1260 system to choose the most appropriate test runner given desired
1261 thread count and OS type.
1262
Todd Fiala68615ce2015-09-15 21:38:04 +00001263 @param results_formatter if specified, provides the TestResultsFormatter
1264 instance that will format and output test result data from the
1265 side-channel test results. When specified, inferior dotest calls
1266 will send test results side-channel data over a socket to the parallel
1267 test runner, which will forward them on to results_formatter.
Todd Fialafed95662015-09-03 18:58:44 +00001268 """
1269
Todd Fiala1cc97b42015-09-21 05:42:26 +00001270 # Do not shut down on sighup.
1271 signal.signal(signal.SIGHUP, signal.SIG_IGN)
1272
Todd Fialafed95662015-09-03 18:58:44 +00001273 dotest_argv = sys.argv[1:]
1274
Greg Claytonb0d148e2015-09-21 17:25:01 +00001275 global output_on_success, RESULTS_FORMATTER
Todd Fialafed95662015-09-03 18:58:44 +00001276 output_on_success = print_details_on_success
Todd Fiala68615ce2015-09-15 21:38:04 +00001277 RESULTS_FORMATTER = results_formatter
Todd Fialafed95662015-09-03 18:58:44 +00001278
Vince Harrond5fa1022015-05-10 15:24:12 +00001279 # We can't use sys.path[0] to determine the script directory
1280 # because it doesn't work under a debugger
Vince Harron8994fed2015-05-22 19:49:23 +00001281 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +00001282 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +00001283 dotest_options = dotest_args.parse_args(parser, dotest_argv)
1284
Todd Fiala68615ce2015-09-15 21:38:04 +00001285 adjust_inferior_options(dotest_argv)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001286
1287 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +00001288
Vince Harrone06a7a82015-05-12 23:12:19 +00001289 # The root directory was specified on the command line
Todd Fiala68615ce2015-09-15 21:38:04 +00001290 test_directory = os.path.dirname(os.path.realpath(__file__))
Todd Fialafed95662015-09-03 18:58:44 +00001291 if test_subdir and len(test_subdir) > 0:
1292 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +00001293 else:
Todd Fialafed95662015-09-03 18:58:44 +00001294 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +00001295
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001296 # clean core files in test tree from previous runs (Linux)
1297 cores = find('core.*', test_subdir)
1298 for core in cores:
1299 os.unlink(core)
1300
Daniel Maleab42556f2013-04-19 18:32:53 +00001301 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +00001302
1303 # Figure out which testrunner strategy we'll use.
1304 runner_strategies_by_name = get_test_runner_strategies(num_threads)
1305
1306 # If the user didn't specify a test runner strategy, determine
1307 # the default now based on number of threads and OS type.
1308 if not test_runner_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001309 test_runner_name = default_test_runner_name(num_threads)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001310
1311 if test_runner_name not in runner_strategies_by_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001312 raise Exception(
1313 "specified testrunner name '{}' unknown. Valid choices: {}".format(
1314 test_runner_name,
1315 runner_strategies_by_name.keys()))
Todd Fiala8cbeed32015-09-08 22:22:33 +00001316 test_runner_func = runner_strategies_by_name[test_runner_name]
1317
1318 summary_results = walk_and_invoke(
Todd Fiala871b2e52015-09-22 22:47:34 +00001319 test_directory, test_subdir, dotest_argv,
1320 num_threads, test_runner_func)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001321
1322 (timed_out, passed, failed, unexpected_successes, pass_count,
1323 fail_count) = summary_results
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +00001324
Todd Fialade9a44e2015-09-22 00:15:50 +00001325 # The results formatter - if present - is done now. Tell it to
1326 # terminate.
1327 if results_formatter is not None:
1328 results_formatter.send_terminate_as_needed()
1329
Vince Harron17f429f2014-12-13 00:08:19 +00001330 timed_out = set(timed_out)
Chaoren Line80372a2015-08-12 18:02:53 +00001331 num_test_files = len(passed) + len(failed)
1332 num_test_cases = pass_count + fail_count
Daniel Maleab42556f2013-04-19 18:32:53 +00001333
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001334 # move core files into session dir
1335 cores = find('core.*', test_subdir)
1336 for core in cores:
1337 dst = core.replace(test_directory, "")[1:]
1338 dst = dst.replace(os.path.sep, "-")
1339 os.rename(core, os.path.join(session_dir, dst))
1340
Vince Harron06381732015-05-12 23:10:36 +00001341 # remove expected timeouts from failures
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001342 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
Vince Harron06381732015-05-12 23:10:36 +00001343 for xtime in expected_timeout:
1344 if xtime in timed_out:
1345 timed_out.remove(xtime)
1346 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001347 result = "ExpectedTimeout"
1348 elif xtime in passed:
1349 result = "UnexpectedCompletion"
1350 else:
1351 result = None # failed
1352
1353 if result:
1354 test_name = os.path.splitext(xtime)[0]
1355 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +00001356
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +00001357 print
Chaoren Lin5a59e462015-08-12 18:02:51 +00001358 sys.stdout.write("Ran %d test suites" % num_test_files)
1359 if num_test_files > 0:
1360 sys.stdout.write(" (%d failed) (%f%%)" % (
1361 len(failed), 100.0 * len(failed) / num_test_files))
1362 print
Chaoren Line80372a2015-08-12 18:02:53 +00001363 sys.stdout.write("Ran %d test cases" % num_test_cases)
1364 if num_test_cases > 0:
Chaoren Lin5a59e462015-08-12 18:02:51 +00001365 sys.stdout.write(" (%d failed) (%f%%)" % (
Chaoren Line80372a2015-08-12 18:02:53 +00001366 fail_count, 100.0 * fail_count / num_test_cases))
Chaoren Lin5a59e462015-08-12 18:02:51 +00001367 print
Zachary Turner4cceca72015-08-14 16:45:32 +00001368 exit_code = 0
1369
Daniel Maleacbaef262013-02-15 21:31:37 +00001370 if len(failed) > 0:
Shawn Best13491c42014-10-22 19:29:00 +00001371 failed.sort()
Ying Chen10ed1a92015-05-28 23:51:49 +00001372 print "Failing Tests (%d)" % len(failed)
Daniel Maleacbaef262013-02-15 21:31:37 +00001373 for f in failed:
Vince Harron17f429f2014-12-13 00:08:19 +00001374 print "%s: LLDB (suite) :: %s (%s)" % (
1375 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
1376 )
Zachary Turner4cceca72015-08-14 16:45:32 +00001377 exit_code = 1
1378
1379 if len(unexpected_successes) > 0:
1380 unexpected_successes.sort()
1381 print "\nUnexpected Successes (%d)" % len(unexpected_successes)
1382 for u in unexpected_successes:
1383 print "UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info)
1384
1385 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001386
1387if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +00001388 sys.stderr.write(
1389 "error: dosep.py no longer supports being called directly. "
1390 "Please call dotest.py directly. The dosep.py-specific arguments "
1391 "have been added under the Parallel processing arguments.\n")
1392 sys.exit(128)