blob: facd804a3d852d7c799b58d0daa4397bb9cd13d1 [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
Zachary Turnerff890da2015-10-19 23:45:41 +000035from __future__ import print_function
36
Todd Fiala2d3754d2015-09-29 22:19:06 +000037# system packages and modules
Todd Fiala68615ce2015-09-15 21:38:04 +000038import asyncore
Todd Fiala83c32e32015-09-22 21:19:40 +000039import distutils.version
Vince Harrondcc2b9f2015-05-27 04:40:36 +000040import fnmatch
Todd Fiala8cbeed32015-09-08 22:22:33 +000041import multiprocessing
42import multiprocessing.pool
43import os
Todd Fiala3f0a3602014-07-08 06:42:37 +000044import platform
Todd Fiala8cbeed32015-09-08 22:22:33 +000045import Queue
Vince Harron06381732015-05-12 23:10:36 +000046import re
Todd Fiala8cbeed32015-09-08 22:22:33 +000047import signal
Todd Fiala3f0a3602014-07-08 06:42:37 +000048import sys
Todd Fiala8cbeed32015-09-08 22:22:33 +000049import threading
Todd Fiala2d3754d2015-09-29 22:19:06 +000050
51# Add our local test_runner/lib dir to the python path.
52sys.path.append(os.path.join(os.path.dirname(__file__), "test_runner", "lib"))
53
54# Our packages and modules
Todd Fiala68615ce2015-09-15 21:38:04 +000055import dotest_channels
Todd Fiala8cbeed32015-09-08 22:22:33 +000056import dotest_args
Todd Fiala2d3754d2015-09-29 22:19:06 +000057import lldb_utils
58import process_control
Vince Harron17f429f2014-12-13 00:08:19 +000059
Vince Harron17f429f2014-12-13 00:08:19 +000060# Status codes for running command with timeout.
61eTimedOut, ePassed, eFailed = 124, 0, 1
62
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000063output_lock = None
64test_counter = None
65total_tests = None
Chaoren Linffc63b02015-08-12 18:02:49 +000066test_name_len = None
Pavel Labath05ab2372015-07-06 15:57:52 +000067dotest_options = None
Zachary Turner38e64172015-08-10 17:46:11 +000068output_on_success = False
Todd Fiala68615ce2015-09-15 21:38:04 +000069RESULTS_FORMATTER = None
70RUNNER_PROCESS_ASYNC_MAP = None
71RESULTS_LISTENER_CHANNEL = None
Chaoren Linb6325d02015-08-12 18:02:54 +000072
Todd Fiala33896a92015-09-18 21:01:13 +000073"""Contains an optional function pointer that can return the worker index
74 for the given thread/process calling it. Returns a 0-based index."""
75GET_WORKER_INDEX = None
76
Todd Fiala1cc97b42015-09-21 05:42:26 +000077
Todd Fiala33896a92015-09-18 21:01:13 +000078def setup_global_variables(
79 lock, counter, total, name_len, options, worker_index_map):
Chaoren Linffc63b02015-08-12 18:02:49 +000080 global output_lock, test_counter, total_tests, test_name_len
81 global dotest_options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000082 output_lock = lock
83 test_counter = counter
84 total_tests = total
Chaoren Linffc63b02015-08-12 18:02:49 +000085 test_name_len = name_len
Pavel Labath05ab2372015-07-06 15:57:52 +000086 dotest_options = options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000087
Todd Fiala33896a92015-09-18 21:01:13 +000088 if worker_index_map is not None:
89 # We'll use the output lock for this to avoid sharing another lock.
90 # This won't be used much.
91 index_lock = lock
92
93 def get_worker_index_use_pid():
94 """Returns a 0-based, process-unique index for the worker."""
95 pid = os.getpid()
96 with index_lock:
97 if pid not in worker_index_map:
98 worker_index_map[pid] = len(worker_index_map)
99 return worker_index_map[pid]
100
101 global GET_WORKER_INDEX
102 GET_WORKER_INDEX = get_worker_index_use_pid
103
Chaoren Linb6325d02015-08-12 18:02:54 +0000104
Zachary Turner38e64172015-08-10 17:46:11 +0000105def report_test_failure(name, command, output):
106 global output_lock
107 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000108 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
Zachary Turnerff890da2015-10-19 23:45:41 +0000109 print(file=sys.stderr)
110 print(output, file=sys.stderr)
111 print("[%s FAILED]" % name, file=sys.stderr)
112 print("Command invoked: %s" % ' '.join(command), file=sys.stderr)
Chaoren Linffc63b02015-08-12 18:02:49 +0000113 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000114
Chaoren Linb6325d02015-08-12 18:02:54 +0000115
Zachary Turner38e64172015-08-10 17:46:11 +0000116def report_test_pass(name, output):
117 global output_lock, output_on_success
118 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000119 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
120 if output_on_success:
Zachary Turnerff890da2015-10-19 23:45:41 +0000121 print(file=sys.stderr)
122 print(output, file=sys.stderr)
123 print("[%s PASSED]" % name, file=sys.stderr)
Chaoren Linffc63b02015-08-12 18:02:49 +0000124 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000125
Chaoren Linb6325d02015-08-12 18:02:54 +0000126
Chaoren Linffc63b02015-08-12 18:02:49 +0000127def update_progress(test_name=""):
128 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000129 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000130 counter_len = len(str(total_tests))
Greg Clayton1827fc22015-09-19 00:39:09 +0000131 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
132 sys.stderr.write(
133 "\r%*d out of %d test suites processed - %-*s" %
134 (counter_len, test_counter.value, total_tests,
135 test_name_len.value, test_name))
Chaoren Linffc63b02015-08-12 18:02:49 +0000136 if len(test_name) > test_name_len.value:
137 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000138 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000139 sys.stdout.flush()
140 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000141
Chaoren Linb6325d02015-08-12 18:02:54 +0000142
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000143def parse_test_results(output):
144 passes = 0
145 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000146 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000147 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000148 pass_count = re.search("^RESULT:.*([0-9]+) passes",
149 result, re.MULTILINE)
150 fail_count = re.search("^RESULT:.*([0-9]+) failures",
151 result, re.MULTILINE)
152 error_count = re.search("^RESULT:.*([0-9]+) errors",
153 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000154 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
155 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000156 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000157 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000158 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000159 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000160 if unexpected_success_count is not None:
161 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000162 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000163 failures = failures + int(error_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000164 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000165
Chaoren Linb6325d02015-08-12 18:02:54 +0000166
Todd Fiala2d3754d2015-09-29 22:19:06 +0000167class DoTestProcessDriver(process_control.ProcessDriver):
168 """Drives the dotest.py inferior process and handles bookkeeping."""
169 def __init__(self, output_file, output_file_lock, pid_events, file_name,
170 soft_terminate_timeout):
171 super(DoTestProcessDriver, self).__init__(
172 soft_terminate_timeout=soft_terminate_timeout)
173 self.output_file = output_file
174 self.output_lock = lldb_utils.OptionalWith(output_file_lock)
175 self.pid_events = pid_events
176 self.results = None
177 self.file_name = file_name
178
179 def write(self, content):
180 with self.output_lock:
181 self.output_file.write(content)
182
183 def on_process_started(self):
184 if self.pid_events:
185 self.pid_events.put_nowait(('created', self.process.pid))
186
187 def on_process_exited(self, command, output, was_timeout, exit_status):
188 if self.pid_events:
189 # No point in culling out those with no exit_status (i.e.
190 # those we failed to kill). That would just cause
191 # downstream code to try to kill it later on a Ctrl-C. At
192 # this point, a best-effort-to-kill already took place. So
193 # call it destroyed here.
194 self.pid_events.put_nowait(('destroyed', self.process.pid))
195
196 # Override the exit status if it was a timeout.
197 if was_timeout:
198 exit_status = eTimedOut
199
200 # If we didn't end up with any output, call it empty for
201 # stdout/stderr.
202 if output is None:
203 output = ('', '')
204
205 # Now parse the output.
206 passes, failures, unexpected_successes = parse_test_results(output)
207 if exit_status == 0:
208 # stdout does not have any useful information from 'dotest.py',
209 # only stderr does.
210 report_test_pass(self.file_name, output[1])
211 else:
212 report_test_failure(self.file_name, command, output[1])
213
214 # Save off the results for the caller.
215 self.results = (
216 self.file_name,
217 exit_status,
218 passes,
219 failures,
220 unexpected_successes)
221
222
223def get_soft_terminate_timeout():
224 # Defaults to 10 seconds, but can set
225 # LLDB_TEST_SOFT_TERMINATE_TIMEOUT to a floating point
226 # number in seconds. This value indicates how long
227 # the test runner will wait for the dotest inferior to
228 # handle a timeout via a soft terminate before it will
229 # assume that failed and do a hard terminate.
230
231 # TODO plumb through command-line option
232 return float(os.environ.get('LLDB_TEST_SOFT_TERMINATE_TIMEOUT', 10.0))
233
234
235def want_core_on_soft_terminate():
236 # TODO plumb through command-line option
237 if platform.system() == 'Linux':
238 return True
239 else:
240 return False
Todd Fialada817b62015-09-22 18:05:11 +0000241
242
Todd Fiala8cbeed32015-09-08 22:22:33 +0000243def call_with_timeout(command, timeout, name, inferior_pid_events):
Todd Fiala2d3754d2015-09-29 22:19:06 +0000244 # Add our worker index (if we have one) to all test events
245 # from this inferior.
Todd Fiala33896a92015-09-18 21:01:13 +0000246 if GET_WORKER_INDEX is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000247 try:
248 worker_index = GET_WORKER_INDEX()
249 command.extend([
Todd Fiala2d3754d2015-09-29 22:19:06 +0000250 "--event-add-entries",
251 "worker_index={}:int".format(worker_index)])
252 except: # pylint: disable=bare-except
253 # Ctrl-C does bad things to multiprocessing.Manager.dict()
254 # lookup. Just swallow it.
Todd Fialae83f1402015-09-18 22:45:31 +0000255 pass
256
Todd Fiala2d3754d2015-09-29 22:19:06 +0000257 # Create the inferior dotest.py ProcessDriver.
258 soft_terminate_timeout = get_soft_terminate_timeout()
259 want_core = want_core_on_soft_terminate()
Todd Fiala68615ce2015-09-15 21:38:04 +0000260
Todd Fiala2d3754d2015-09-29 22:19:06 +0000261 process_driver = DoTestProcessDriver(
262 sys.stdout,
263 output_lock,
264 inferior_pid_events,
265 name,
266 soft_terminate_timeout)
Todd Fiala68615ce2015-09-15 21:38:04 +0000267
Todd Fiala2d3754d2015-09-29 22:19:06 +0000268 # Run it with a timeout.
269 process_driver.run_command_with_timeout(command, timeout, want_core)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000270
Todd Fiala2d3754d2015-09-29 22:19:06 +0000271 # Return the results.
272 if not process_driver.results:
273 # This is truly exceptional. Even a failing or timed out
274 # binary should have called the results-generation code.
275 raise Exception("no test results were generated whatsoever")
276 return process_driver.results
Johnny Chene8d9dc62011-10-31 19:04:07 +0000277
Chaoren Linb6325d02015-08-12 18:02:54 +0000278
Todd Fiala8cbeed32015-09-08 22:22:33 +0000279def process_dir(root, files, test_root, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000280 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000281 results = []
Steve Puccibefe2b12014-03-07 00:01:11 +0000282 for name in files:
Zachary Turnerf6896b02015-01-05 19:37:03 +0000283 script_file = os.path.join(test_root, "dotest.py")
Zachary Turnerf6896b02015-01-05 19:37:03 +0000284 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000285 dotest_argv +
Todd Fialafed95662015-09-03 18:58:44 +0000286 ["--inferior", "-p", name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000287
288 timeout_name = os.path.basename(os.path.splitext(name)[0]).upper()
289
Chaoren Linb6325d02015-08-12 18:02:54 +0000290 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
291 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000292
Todd Fiala8cbeed32015-09-08 22:22:33 +0000293 results.append(call_with_timeout(
294 command, timeout, name, inferior_pid_events))
Vince Harron17f429f2014-12-13 00:08:19 +0000295
Zachary Turner4cceca72015-08-14 16:45:32 +0000296 # result = (name, status, passes, failures, unexpected_successes)
297 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000298 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000299 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000300 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000301 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000302 if status != ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000303 unexpected_passes = [name for name, _, _, _, unexpected_successes in results
304 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000305
Chaoren Line80372a2015-08-12 18:02:53 +0000306 pass_count = sum([result[2] for result in results])
307 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000308
Zachary Turner4cceca72015-08-14 16:45:32 +0000309 return (timed_out, passed, failed, unexpected_passes, pass_count, fail_count)
Steve Puccibefe2b12014-03-07 00:01:11 +0000310
311in_q = None
312out_q = None
313
Chaoren Linb6325d02015-08-12 18:02:54 +0000314
Todd Fiala8cbeed32015-09-08 22:22:33 +0000315def process_dir_worker_multiprocessing(
316 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000317 a_dotest_options, job_queue, result_queue, inferior_pid_events,
318 worker_index_map):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000319 """Worker thread main loop when in multiprocessing mode.
Steve Puccibefe2b12014-03-07 00:01:11 +0000320 Takes one directory specification at a time and works on it."""
Todd Fiala8cbeed32015-09-08 22:22:33 +0000321
322 # Shut off interrupt handling in the child process.
323 signal.signal(signal.SIGINT, signal.SIG_IGN)
Ying Chend93aa102015-09-23 21:53:18 +0000324 if hasattr(signal, 'SIGHUP'):
325 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000326
327 # Setup the global state for the worker process.
328 setup_global_variables(
329 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000330 a_dotest_options, worker_index_map)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000331
332 # Keep grabbing entries from the queue until done.
333 while not job_queue.empty():
334 try:
335 job = job_queue.get(block=False)
336 result = process_dir(job[0], job[1], job[2], job[3],
337 inferior_pid_events)
338 result_queue.put(result)
339 except Queue.Empty:
340 # Fine, we're done.
341 pass
Steve Puccibefe2b12014-03-07 00:01:11 +0000342
Chaoren Linb6325d02015-08-12 18:02:54 +0000343
Todd Fiala8cbeed32015-09-08 22:22:33 +0000344def process_dir_worker_multiprocessing_pool(args):
345 return process_dir(*args)
346
347
Todd Fiala68615ce2015-09-15 21:38:04 +0000348def process_dir_worker_threading(job_queue, result_queue, inferior_pid_events):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000349 """Worker thread main loop when in threading mode.
350
351 This one supports the hand-rolled pooling support.
352
353 Takes one directory specification at a time and works on it."""
354
355 # Keep grabbing entries from the queue until done.
356 while not job_queue.empty():
357 try:
358 job = job_queue.get(block=False)
359 result = process_dir(job[0], job[1], job[2], job[3],
360 inferior_pid_events)
361 result_queue.put(result)
362 except Queue.Empty:
363 # Fine, we're done.
364 pass
365
366
367def process_dir_worker_threading_pool(args):
368 return process_dir(*args)
369
370
371def process_dir_mapper_inprocess(args):
372 """Map adapter for running the subprocess-based, non-threaded test runner.
373
374 @param args the process work item tuple
375 @return the test result tuple
376 """
377 return process_dir(*args)
378
379
380def collect_active_pids_from_pid_events(event_queue):
381 """
382 Returns the set of what should be active inferior pids based on
383 the event stream.
384
385 @param event_queue a multiprocessing.Queue containing events of the
386 form:
387 ('created', pid)
388 ('destroyed', pid)
389
390 @return set of inferior dotest.py pids activated but never completed.
391 """
392 active_pid_set = set()
393 while not event_queue.empty():
394 pid_event = event_queue.get_nowait()
395 if pid_event[0] == 'created':
396 active_pid_set.add(pid_event[1])
397 elif pid_event[0] == 'destroyed':
398 active_pid_set.remove(pid_event[1])
399 return active_pid_set
400
401
402def kill_all_worker_processes(workers, inferior_pid_events):
403 """
404 Kills all specified worker processes and their process tree.
405
406 @param workers a list of multiprocess.Process worker objects.
407 @param inferior_pid_events a multiprocess.Queue that contains
408 all inferior create and destroy events. Used to construct
409 the list of child pids still outstanding that need to be killed.
410 """
411 for worker in workers:
412 worker.terminate()
413 worker.join()
414
415 # Add all the child test pids created.
416 active_pid_set = collect_active_pids_from_pid_events(
417 inferior_pid_events)
418 for inferior_pid in active_pid_set:
Zachary Turnerff890da2015-10-19 23:45:41 +0000419 print("killing inferior pid {}".format(inferior_pid))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000420 os.kill(inferior_pid, signal.SIGKILL)
421
422
423def kill_all_worker_threads(workers, inferior_pid_events):
424 """
425 Kills all specified worker threads and their process tree.
426
427 @param workers a list of multiprocess.Process worker objects.
428 @param inferior_pid_events a multiprocess.Queue that contains
429 all inferior create and destroy events. Used to construct
430 the list of child pids still outstanding that need to be killed.
431 """
432
433 # Add all the child test pids created.
434 active_pid_set = collect_active_pids_from_pid_events(
435 inferior_pid_events)
436 for inferior_pid in active_pid_set:
Zachary Turnerff890da2015-10-19 23:45:41 +0000437 print("killing inferior pid {}".format(inferior_pid))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000438 os.kill(inferior_pid, signal.SIGKILL)
439
440 # We don't have a way to nuke the threads. However, since we killed
441 # all the inferiors, and we drained the job queue, this will be
442 # good enough. Wait cleanly for each worker thread to wrap up.
443 for worker in workers:
444 worker.join()
445
446
447def find_test_files_in_dir_tree(dir_root, found_func):
448 """Calls found_func for all the test files in the given dir hierarchy.
449
450 @param dir_root the path to the directory to start scanning
451 for test files. All files in this directory and all its children
452 directory trees will be searched.
453
454 @param found_func a callable object that will be passed
455 the parent directory (relative to dir_root) and the list of
456 test files from within that directory.
457 """
458 for root, _, files in os.walk(dir_root, topdown=False):
459 def is_test_filename(test_dir, base_filename):
460 """Returns True if the given filename matches the test name format.
461
462 @param test_dir the directory to check. Should be absolute or
463 relative to current working directory.
464
465 @param base_filename the base name of the filename to check for a
466 dherence to the python test case filename format.
467
468 @return True if name matches the python test case filename format.
469 """
470 # Not interested in symbolically linked files.
471 if os.path.islink(os.path.join(test_dir, base_filename)):
472 return False
473 # Only interested in test files with the "Test*.py" naming pattern.
474 return (base_filename.startswith("Test") and
475 base_filename.endswith(".py"))
476
477 tests = [filename for filename in files
478 if is_test_filename(root, filename)]
479 if tests:
480 found_func(root, tests)
481
482
483def initialize_global_vars_common(num_threads, test_work_items):
484 global total_tests, test_counter, test_name_len
Greg Clayton1827fc22015-09-19 00:39:09 +0000485
Todd Fiala8cbeed32015-09-08 22:22:33 +0000486 total_tests = sum([len(item[1]) for item in test_work_items])
487 test_counter = multiprocessing.Value('i', 0)
488 test_name_len = multiprocessing.Value('i', 0)
Greg Clayton1827fc22015-09-19 00:39:09 +0000489 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
Zachary Turnerff890da2015-10-19 23:45:41 +0000490 print("Testing: %d test suites, %d thread%s" % (
491 total_tests, num_threads, (num_threads > 1) * "s"), file=sys.stderr)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000492 update_progress()
493
494
495def initialize_global_vars_multiprocessing(num_threads, test_work_items):
496 # Initialize the global state we'll use to communicate with the
497 # rest of the flat module.
498 global output_lock
499 output_lock = multiprocessing.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000500
Todd Fiala8cbeed32015-09-08 22:22:33 +0000501 initialize_global_vars_common(num_threads, test_work_items)
502
503
504def initialize_global_vars_threading(num_threads, test_work_items):
Todd Fiala33896a92015-09-18 21:01:13 +0000505 """Initializes global variables used in threading mode.
506 @param num_threads specifies the number of workers used.
507 @param test_work_items specifies all the work items
508 that will be processed.
509 """
Todd Fiala8cbeed32015-09-08 22:22:33 +0000510 # Initialize the global state we'll use to communicate with the
511 # rest of the flat module.
512 global output_lock
513 output_lock = threading.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000514
515 index_lock = threading.RLock()
516 index_map = {}
517
518 def get_worker_index_threading():
519 """Returns a 0-based, thread-unique index for the worker thread."""
520 thread_id = threading.current_thread().ident
521 with index_lock:
522 if thread_id not in index_map:
523 index_map[thread_id] = len(index_map)
524 return index_map[thread_id]
525
526
527 global GET_WORKER_INDEX
528 GET_WORKER_INDEX = get_worker_index_threading
529
Todd Fiala8cbeed32015-09-08 22:22:33 +0000530 initialize_global_vars_common(num_threads, test_work_items)
531
532
Todd Fiala68615ce2015-09-15 21:38:04 +0000533def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler):
534 """Provides a main loop that is Ctrl-C protected.
535
536 The main loop calls the main_op_func() repeatedly until done_func()
537 returns true. The ctrl_c_handler() method is called with a single
538 int parameter that contains the number of times the ctrl_c has been
539 hit (starting with 1). The ctrl_c_handler() should mutate whatever
540 it needs to have the done_func() return True as soon as it is desired
541 to exit the loop.
542 """
543 done = False
544 ctrl_c_count = 0
545
546 while not done:
547 try:
548 # See if we're done. Start with done check since it is
549 # the first thing executed after a Ctrl-C handler in the
550 # following loop.
551 done = done_func()
552 if not done:
553 # Run the main op once.
554 main_op_func()
555
556 except KeyboardInterrupt:
557 ctrl_c_count += 1
558 ctrl_c_handler(ctrl_c_count)
559
560
561def pump_workers_and_asyncore_map(workers, asyncore_map):
562 """Prunes out completed workers and maintains the asyncore loop.
563
564 The asyncore loop contains the optional socket listener
565 and handlers. When all workers are complete, this method
566 takes care of stopping the listener. It also runs the
567 asyncore loop for the given async map for 10 iterations.
568
569 @param workers the list of worker Thread/Process instances.
570
571 @param asyncore_map the asyncore threading-aware map that
572 indicates which channels are in use and still alive.
573 """
574
575 # Check on all the workers, removing them from the workers
576 # list as they complete.
577 dead_workers = []
578 for worker in workers:
579 # This non-blocking join call is what allows us
580 # to still receive keyboard interrupts.
581 worker.join(0.01)
582 if not worker.is_alive():
583 dead_workers.append(worker)
584 # Clear out the completed workers
585 for dead_worker in dead_workers:
586 workers.remove(dead_worker)
587
588 # If there are no more workers and there is a listener,
589 # close the listener.
590 global RESULTS_LISTENER_CHANNEL
591 if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None:
592 RESULTS_LISTENER_CHANNEL.close()
593 RESULTS_LISTENER_CHANNEL = None
594
595 # Pump the asyncore map if it isn't empty.
596 if len(asyncore_map) > 0:
597 asyncore.loop(0.1, False, asyncore_map, 10)
598
599
600def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events,
601 stop_all_inferiors_func):
602 """Performs the appropriate ctrl-c action for non-pool parallel test runners
603
604 @param ctrl_c_count starting with 1, indicates the number of times ctrl-c
605 has been intercepted. The value is 1 on the first intercept, 2 on the
606 second, etc.
607
608 @param job_queue a Queue object that contains the work still outstanding
609 (i.e. hasn't been assigned to a worker yet).
610
611 @param workers list of Thread or Process workers.
612
613 @param inferior_pid_events specifies a Queue of inferior process
614 construction and destruction events. Used to build the list of inferior
615 processes that should be killed if we get that far.
616
617 @param stop_all_inferiors_func a callable object that takes the
618 workers and inferior_pid_events parameters (in that order) if a hard
619 stop is to be used on the workers.
620 """
621
622 # Print out which Ctrl-C we're handling.
623 key_name = [
624 "first",
625 "second",
626 "third",
627 "many"]
628
629 if ctrl_c_count < len(key_name):
630 name_index = ctrl_c_count - 1
631 else:
632 name_index = len(key_name) - 1
633 message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index])
634 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000635 print(message)
Todd Fiala68615ce2015-09-15 21:38:04 +0000636
637 if ctrl_c_count == 1:
638 # Remove all outstanding items from the work queue so we stop
639 # doing any more new work.
640 while not job_queue.empty():
641 try:
642 # Just drain it to stop more work from being started.
643 job_queue.get_nowait()
644 except Queue.Empty:
645 pass
646 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000647 print("Stopped more work from being started.")
Todd Fiala68615ce2015-09-15 21:38:04 +0000648 elif ctrl_c_count == 2:
649 # Try to stop all inferiors, even the ones currently doing work.
650 stop_all_inferiors_func(workers, inferior_pid_events)
651 else:
652 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000653 print("All teardown activities kicked off, should finish soon.")
Todd Fiala68615ce2015-09-15 21:38:04 +0000654
655
656def workers_and_async_done(workers, async_map):
657 """Returns True if the workers list and asyncore channels are all done.
658
659 @param workers list of workers (threads/processes). These must adhere
660 to the threading Thread or multiprocessing.Process interface.
661
662 @param async_map the threading-aware asyncore channel map to check
663 for live channels.
664
665 @return False if the workers list exists and has any entries in it, or
666 if the async_map exists and has any entries left in it; otherwise, True.
667 """
668 if workers is not None and len(workers) > 0:
669 # We're not done if we still have workers left.
670 return False
671 if async_map is not None and len(async_map) > 0:
672 return False
673 # We're done.
674 return True
675
676
Todd Fiala8cbeed32015-09-08 22:22:33 +0000677def multiprocessing_test_runner(num_threads, test_work_items):
678 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
679
680 This concurrent test runner is based on the multiprocessing
681 library, and rolls its own worker pooling strategy so it
682 can handle Ctrl-C properly.
683
684 This test runner is known to have an issue running on
685 Windows platforms.
686
687 @param num_threads the number of worker processes to use.
688
689 @param test_work_items the iterable of test work item tuples
690 to run.
691 """
692
693 # Initialize our global state.
694 initialize_global_vars_multiprocessing(num_threads, test_work_items)
695
696 # Create jobs.
697 job_queue = multiprocessing.Queue(len(test_work_items))
698 for test_work_item in test_work_items:
699 job_queue.put(test_work_item)
700
701 result_queue = multiprocessing.Queue(len(test_work_items))
702
703 # Create queues for started child pids. Terminating
704 # the multiprocess processes does not terminate the
705 # child processes they spawn. We can remove this tracking
706 # if/when we move to having the multiprocess process directly
707 # perform the test logic. The Queue size needs to be able to
708 # hold 2 * (num inferior dotest.py processes started) entries.
709 inferior_pid_events = multiprocessing.Queue(4096)
710
Todd Fiala33896a92015-09-18 21:01:13 +0000711 # Worker dictionary allows each worker to figure out its worker index.
712 manager = multiprocessing.Manager()
713 worker_index_map = manager.dict()
714
Todd Fiala8cbeed32015-09-08 22:22:33 +0000715 # Create workers. We don't use multiprocessing.Pool due to
716 # challenges with handling ^C keyboard interrupts.
717 workers = []
718 for _ in range(num_threads):
719 worker = multiprocessing.Process(
720 target=process_dir_worker_multiprocessing,
721 args=(output_lock,
722 test_counter,
723 total_tests,
724 test_name_len,
725 dotest_options,
726 job_queue,
727 result_queue,
Todd Fiala33896a92015-09-18 21:01:13 +0000728 inferior_pid_events,
729 worker_index_map))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000730 worker.start()
731 workers.append(worker)
732
Todd Fiala68615ce2015-09-15 21:38:04 +0000733 # Main loop: wait for all workers to finish and wait for
734 # the socket handlers to wrap up.
735 ctrl_c_loop(
736 # Main operation of loop
737 lambda: pump_workers_and_asyncore_map(
738 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000739
Todd Fiala68615ce2015-09-15 21:38:04 +0000740 # Return True when we're done with the main loop.
741 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000742
Todd Fiala68615ce2015-09-15 21:38:04 +0000743 # Indicate what we do when we receive one or more Ctrl-Cs.
744 lambda ctrl_c_count: handle_ctrl_c(
745 ctrl_c_count, job_queue, workers, inferior_pid_events,
746 kill_all_worker_processes))
747
748 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000749 test_results = []
750 while not result_queue.empty():
751 test_results.append(result_queue.get(block=False))
752 return test_results
753
754
Todd Fiala68615ce2015-09-15 21:38:04 +0000755def map_async_run_loop(future, channel_map, listener_channel):
756 """Blocks until the Pool.map_async completes and the channel completes.
757
758 @param future an AsyncResult instance from a Pool.map_async() call.
759
760 @param channel_map the asyncore dispatch channel map that should be pumped.
761 Optional: may be None.
762
763 @param listener_channel the channel representing a listener that should be
764 closed once the map_async results are available.
765
766 @return the results from the async_result instance.
767 """
768 map_results = None
769
770 done = False
771 while not done:
772 # Check if we need to reap the map results.
773 if map_results is None:
774 if future.ready():
775 # Get the results.
776 map_results = future.get()
777
778 # Close the runner process listener channel if we have
779 # one: no more connections will be incoming.
780 if listener_channel is not None:
781 listener_channel.close()
782
783 # Pump the asyncore loop if we have a listener socket.
784 if channel_map is not None:
785 asyncore.loop(0.01, False, channel_map, 10)
786
787 # Figure out if we're done running.
788 done = map_results is not None
789 if channel_map is not None:
790 # We have a runner process async map. Check if it
791 # is complete.
792 if len(channel_map) > 0:
793 # We still have an asyncore channel running. Not done yet.
794 done = False
795
796 return map_results
797
798
Todd Fiala8cbeed32015-09-08 22:22:33 +0000799def multiprocessing_test_runner_pool(num_threads, test_work_items):
800 # Initialize our global state.
801 initialize_global_vars_multiprocessing(num_threads, test_work_items)
802
Todd Fiala33896a92015-09-18 21:01:13 +0000803 manager = multiprocessing.Manager()
804 worker_index_map = manager.dict()
805
Todd Fiala8cbeed32015-09-08 22:22:33 +0000806 pool = multiprocessing.Pool(
807 num_threads,
808 initializer=setup_global_variables,
809 initargs=(output_lock, test_counter, total_tests, test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000810 dotest_options, worker_index_map))
Todd Fiala68615ce2015-09-15 21:38:04 +0000811
812 # Start the map operation (async mode).
813 map_future = pool.map_async(
814 process_dir_worker_multiprocessing_pool, test_work_items)
815 return map_async_run_loop(
816 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000817
818
819def threading_test_runner(num_threads, test_work_items):
820 """Provides hand-wrapped pooling threading-based test runner adapter
821 with Ctrl-C support.
822
823 This concurrent test runner is based on the threading
824 library, and rolls its own worker pooling strategy so it
825 can handle Ctrl-C properly.
826
827 @param num_threads the number of worker processes to use.
828
829 @param test_work_items the iterable of test work item tuples
830 to run.
831 """
832
833 # Initialize our global state.
834 initialize_global_vars_threading(num_threads, test_work_items)
835
836 # Create jobs.
837 job_queue = Queue.Queue()
838 for test_work_item in test_work_items:
839 job_queue.put(test_work_item)
840
841 result_queue = Queue.Queue()
842
843 # Create queues for started child pids. Terminating
844 # the threading threads does not terminate the
845 # child processes they spawn.
846 inferior_pid_events = Queue.Queue()
847
848 # Create workers. We don't use multiprocessing.pool.ThreadedPool
849 # due to challenges with handling ^C keyboard interrupts.
850 workers = []
851 for _ in range(num_threads):
852 worker = threading.Thread(
853 target=process_dir_worker_threading,
Todd Fiala68615ce2015-09-15 21:38:04 +0000854 args=(job_queue,
Todd Fiala8cbeed32015-09-08 22:22:33 +0000855 result_queue,
856 inferior_pid_events))
857 worker.start()
858 workers.append(worker)
859
Todd Fiala68615ce2015-09-15 21:38:04 +0000860 # Main loop: wait for all workers to finish and wait for
861 # the socket handlers to wrap up.
862 ctrl_c_loop(
863 # Main operation of loop
864 lambda: pump_workers_and_asyncore_map(
865 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000866
Todd Fiala68615ce2015-09-15 21:38:04 +0000867 # Return True when we're done with the main loop.
868 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000869
Todd Fiala68615ce2015-09-15 21:38:04 +0000870 # Indicate what we do when we receive one or more Ctrl-Cs.
871 lambda ctrl_c_count: handle_ctrl_c(
872 ctrl_c_count, job_queue, workers, inferior_pid_events,
873 kill_all_worker_threads))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000874
Todd Fiala68615ce2015-09-15 21:38:04 +0000875 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000876 test_results = []
877 while not result_queue.empty():
878 test_results.append(result_queue.get(block=False))
879 return test_results
880
881
882def threading_test_runner_pool(num_threads, test_work_items):
883 # Initialize our global state.
884 initialize_global_vars_threading(num_threads, test_work_items)
885
Todd Fiala68615ce2015-09-15 21:38:04 +0000886 pool = multiprocessing.pool.ThreadPool(num_threads)
887 map_future = pool.map_async(
888 process_dir_worker_threading_pool, test_work_items)
889
890 return map_async_run_loop(
891 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
892
893
894def asyncore_run_loop(channel_map):
895 try:
896 asyncore.loop(None, False, channel_map)
897 except:
898 # Swallow it, we're seeing:
899 # error: (9, 'Bad file descriptor')
900 # when the listener channel is closed. Shouldn't be the case.
901 pass
Todd Fiala8cbeed32015-09-08 22:22:33 +0000902
903
904def inprocess_exec_test_runner(test_work_items):
905 # Initialize our global state.
906 initialize_global_vars_multiprocessing(1, test_work_items)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000907
Todd Fiala33896a92015-09-18 21:01:13 +0000908 # We're always worker index 0
909 global GET_WORKER_INDEX
910 GET_WORKER_INDEX = lambda: 0
911
Todd Fiala68615ce2015-09-15 21:38:04 +0000912 # Run the listener and related channel maps in a separate thread.
913 # global RUNNER_PROCESS_ASYNC_MAP
914 global RESULTS_LISTENER_CHANNEL
915 if RESULTS_LISTENER_CHANNEL is not None:
916 socket_thread = threading.Thread(
917 target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP))
918 socket_thread.start()
919
920 # Do the work.
921 test_results = map(process_dir_mapper_inprocess, test_work_items)
922
923 # If we have a listener channel, shut it down here.
924 if RESULTS_LISTENER_CHANNEL is not None:
925 # Close down the channel.
926 RESULTS_LISTENER_CHANNEL.close()
927 RESULTS_LISTENER_CHANNEL = None
928
929 # Wait for the listener and handlers to complete.
930 socket_thread.join()
931
932 return test_results
Todd Fiala8cbeed32015-09-08 22:22:33 +0000933
934def walk_and_invoke(test_directory, test_subdir, dotest_argv,
Todd Fiala871b2e52015-09-22 22:47:34 +0000935 num_workers, test_runner_func):
Steve Puccibefe2b12014-03-07 00:01:11 +0000936 """Look for matched files and invoke test driver on each one.
937 In single-threaded mode, each test driver is invoked directly.
938 In multi-threaded mode, submit each test driver to a worker
Vince Harrone06a7a82015-05-12 23:12:19 +0000939 queue, and then wait for all to complete.
940
941 test_directory - lldb/test/ directory
Chaoren Linb6325d02015-08-12 18:02:54 +0000942 test_subdir - lldb/test/ or a subfolder with the tests we're interested in
943 running
Vince Harrone06a7a82015-05-12 23:12:19 +0000944 """
Todd Fiala68615ce2015-09-15 21:38:04 +0000945 # The async_map is important to keep all thread-related asyncore
946 # channels distinct when we call asyncore.loop() later on.
947 global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP
948 RUNNER_PROCESS_ASYNC_MAP = {}
949
950 # If we're outputting side-channel test results, create the socket
951 # listener channel and tell the inferior to send results to the
952 # port on which we'll be listening.
953 if RESULTS_FORMATTER is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000954 forwarding_func = RESULTS_FORMATTER.handle_event
Todd Fiala68615ce2015-09-15 21:38:04 +0000955 RESULTS_LISTENER_CHANNEL = (
956 dotest_channels.UnpicklingForwardingListenerChannel(
Todd Fiala871b2e52015-09-22 22:47:34 +0000957 RUNNER_PROCESS_ASYNC_MAP, "localhost", 0,
958 2 * num_workers, forwarding_func))
Todd Fiala68615ce2015-09-15 21:38:04 +0000959 dotest_argv.append("--results-port")
960 dotest_argv.append(str(RESULTS_LISTENER_CHANNEL.address[1]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000961
962 # Collect the test files that we'll run.
963 test_work_items = []
Todd Fiala8cbeed32015-09-08 22:22:33 +0000964 find_test_files_in_dir_tree(
965 test_subdir, lambda testdir, test_files: test_work_items.append([
966 test_subdir, test_files, test_directory, dotest_argv, None]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000967
Todd Fiala8cbeed32015-09-08 22:22:33 +0000968 # Convert test work items into test results using whatever
969 # was provided as the test run function.
970 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +0000971
Todd Fiala8cbeed32015-09-08 22:22:33 +0000972 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +0000973 timed_out = sum([result[0] for result in test_results], [])
974 passed = sum([result[1] for result in test_results], [])
975 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +0000976 unexpected_successes = sum([result[3] for result in test_results], [])
977 pass_count = sum([result[4] for result in test_results])
978 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +0000979
Todd Fiala8cbeed32015-09-08 22:22:33 +0000980 return (timed_out, passed, failed, unexpected_successes, pass_count,
981 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +0000982
Chaoren Linb6325d02015-08-12 18:02:54 +0000983
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000984def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +0000985 # returns a set of test filenames that might timeout
986 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +0000987 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000988 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +0000989 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000990 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000991 m = re.search(r'remote-(\w+)', platform_name)
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000992 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +0000993
994 expected_timeout = set()
995
996 if target.startswith("linux"):
997 expected_timeout |= {
Vince Harron06381732015-05-12 23:10:36 +0000998 "TestConnectRemote.py",
999 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +00001000 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +00001001 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001002
1003 # Times out in ~10% of the times on the build bot
1004 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +00001005 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001006 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +00001007 "TestThreadStepOut.py",
1008 }
1009 elif target.startswith("android"):
1010 expected_timeout |= {
1011 "TestExitDuringStep.py",
1012 "TestHelloWorld.py",
1013 }
Chaoren Linfebef1b2015-08-19 17:22:12 +00001014 if host.startswith("win32"):
1015 expected_timeout |= {
1016 "TestEvents.py",
1017 "TestThreadStates.py",
1018 }
Ed Maste4dd8fba2015-05-14 16:25:52 +00001019 elif target.startswith("freebsd"):
1020 expected_timeout |= {
1021 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +00001022 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +00001023 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +00001024 "TestWatchpointConditionAPI.py",
1025 }
Vince Harron0f173ac2015-05-18 19:36:33 +00001026 elif target.startswith("darwin"):
1027 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +00001028 # times out on MBP Retina, Mid 2012
1029 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +00001030 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +00001031 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +00001032 }
Vince Harron06381732015-05-12 23:10:36 +00001033 return expected_timeout
1034
Chaoren Linb6325d02015-08-12 18:02:54 +00001035
Pavel Labathfad30cf2015-06-29 14:16:51 +00001036def getDefaultTimeout(platform_name):
1037 if os.getenv("LLDB_TEST_TIMEOUT"):
1038 return os.getenv("LLDB_TEST_TIMEOUT")
1039
1040 if platform_name is None:
1041 platform_name = sys.platform
1042
1043 if platform_name.startswith("remote-"):
1044 return "10m"
1045 else:
1046 return "4m"
1047
Chaoren Linb6325d02015-08-12 18:02:54 +00001048
Vince Harron0b9dbb52015-05-21 18:18:52 +00001049def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +00001050 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +00001051 os.utime(fname, times)
1052
Chaoren Linb6325d02015-08-12 18:02:54 +00001053
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001054def find(pattern, path):
1055 result = []
1056 for root, dirs, files in os.walk(path):
1057 for name in files:
1058 if fnmatch.fnmatch(name, pattern):
1059 result.append(os.path.join(root, name))
1060 return result
1061
Chaoren Linb6325d02015-08-12 18:02:54 +00001062
Todd Fiala8cbeed32015-09-08 22:22:33 +00001063def get_test_runner_strategies(num_threads):
1064 """Returns the test runner strategies by name in a dictionary.
1065
1066 @param num_threads specifies the number of threads/processes
1067 that will be used for concurrent test runners.
1068
1069 @return dictionary with key as test runner strategy name and
1070 value set to a callable object that takes the test work item
1071 and returns a test result tuple.
1072 """
1073 return {
1074 # multiprocessing supports ctrl-c and does not use
1075 # multiprocessing.Pool.
1076 "multiprocessing":
1077 (lambda work_items: multiprocessing_test_runner(
1078 num_threads, work_items)),
1079
1080 # multiprocessing-pool uses multiprocessing.Pool but
1081 # does not support Ctrl-C.
1082 "multiprocessing-pool":
1083 (lambda work_items: multiprocessing_test_runner_pool(
1084 num_threads, work_items)),
1085
1086 # threading uses a hand-rolled worker pool much
1087 # like multiprocessing, but instead uses in-process
1088 # worker threads. This one supports Ctrl-C.
1089 "threading":
1090 (lambda work_items: threading_test_runner(num_threads, work_items)),
1091
1092 # threading-pool uses threading for the workers (in-process)
1093 # and uses the multiprocessing.pool thread-enabled pool.
Todd Fiala68615ce2015-09-15 21:38:04 +00001094 # This does not properly support Ctrl-C.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001095 "threading-pool":
1096 (lambda work_items: threading_test_runner_pool(
1097 num_threads, work_items)),
1098
1099 # serial uses the subprocess-based, single process
1100 # test runner. This provides process isolation but
Todd Fiala68615ce2015-09-15 21:38:04 +00001101 # no concurrent test execution.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001102 "serial":
1103 inprocess_exec_test_runner
1104 }
1105
1106
Todd Fialaea736242015-09-23 15:21:28 +00001107def _remove_option(
1108 args, long_option_name, short_option_name, takes_arg):
Todd Fiala68615ce2015-09-15 21:38:04 +00001109 """Removes option and related option arguments from args array.
Todd Fialaea736242015-09-23 15:21:28 +00001110
1111 This method removes all short/long options that match the given
1112 arguments.
1113
Todd Fiala68615ce2015-09-15 21:38:04 +00001114 @param args the array of command line arguments (in/out)
Todd Fialaea736242015-09-23 15:21:28 +00001115
1116 @param long_option_name the full command line representation of the
1117 long-form option that will be removed (including '--').
1118
1119 @param short_option_name the short version of the command line option
1120 that will be removed (including '-').
1121
1122 @param takes_arg True if the option takes an argument.
1123
Todd Fiala68615ce2015-09-15 21:38:04 +00001124 """
Todd Fialaea736242015-09-23 15:21:28 +00001125 if long_option_name is not None:
1126 regex_string = "^" + long_option_name + "="
1127 long_regex = re.compile(regex_string)
1128 if short_option_name is not None:
1129 # Short options we only match the -X and assume
1130 # any arg is one command line argument jammed together.
1131 # i.e. -O--abc=1 is a single argument in the args list.
1132 # We don't handle -O --abc=1, as argparse doesn't handle
1133 # it, either.
1134 regex_string = "^" + short_option_name
1135 short_regex = re.compile(regex_string)
1136
1137 def remove_long_internal():
1138 """Removes one matching long option from args.
1139 @returns True if one was found and removed; False otherwise.
1140 """
1141 try:
1142 index = args.index(long_option_name)
1143 # Handle the exact match case.
1144 if takes_arg:
1145 removal_count = 2
1146 else:
1147 removal_count = 1
1148 del args[index:index+removal_count]
1149 return True
1150 except ValueError:
1151 # Thanks to argparse not handling options with known arguments
1152 # like other options parsing libraries (see
1153 # https://bugs.python.org/issue9334), we need to support the
1154 # --results-formatter-options={second-level-arguments} (note
1155 # the equal sign to fool the first-level arguments parser into
1156 # not treating the second-level arguments as first-level
1157 # options). We're certainly at risk of getting this wrong
1158 # since now we're forced into the business of trying to figure
1159 # out what is an argument (although I think this
1160 # implementation will suffice).
1161 for index in range(len(args)):
1162 match = long_regex.search(args[index])
1163 if match:
1164 del args[index]
1165 return True
1166 return False
1167
1168 def remove_short_internal():
1169 """Removes one matching short option from args.
1170 @returns True if one was found and removed; False otherwise.
1171 """
Todd Fiala68615ce2015-09-15 21:38:04 +00001172 for index in range(len(args)):
Todd Fialaea736242015-09-23 15:21:28 +00001173 match = short_regex.search(args[index])
Todd Fiala68615ce2015-09-15 21:38:04 +00001174 if match:
Todd Fiala68615ce2015-09-15 21:38:04 +00001175 del args[index]
Todd Fialaea736242015-09-23 15:21:28 +00001176 return True
1177 return False
Todd Fiala68615ce2015-09-15 21:38:04 +00001178
Todd Fialaea736242015-09-23 15:21:28 +00001179 removal_count = 0
1180 while long_option_name is not None and remove_long_internal():
1181 removal_count += 1
1182 while short_option_name is not None and remove_short_internal():
1183 removal_count += 1
1184 if removal_count == 0:
1185 raise Exception(
1186 "failed to find at least one of '{}', '{}' in options".format(
1187 long_option_name, short_option_name))
Todd Fiala68615ce2015-09-15 21:38:04 +00001188
1189
1190def adjust_inferior_options(dotest_argv):
1191 """Adjusts the commandline args array for inferiors.
1192
1193 This method adjusts the inferior dotest commandline options based
1194 on the parallel test runner's options. Some of the inferior options
1195 will need to change to properly handle aggregation functionality.
1196 """
1197 global dotest_options
1198
1199 # If we don't have a session directory, create one.
1200 if not dotest_options.s:
1201 # no session log directory, we need to add this to prevent
1202 # every dotest invocation from creating its own directory
1203 import datetime
1204 # The windows platforms don't like ':' in the pathname.
1205 timestamp_started = datetime.datetime.now().strftime("%F-%H_%M_%S")
1206 dotest_argv.append('-s')
1207 dotest_argv.append(timestamp_started)
1208 dotest_options.s = timestamp_started
1209
1210 # Adjust inferior results formatter options - if the parallel
1211 # test runner is collecting into the user-specified test results,
1212 # we'll have inferiors spawn with the --results-port option and
1213 # strip the original test runner options.
1214 if dotest_options.results_file is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001215 _remove_option(dotest_argv, "--results-file", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001216 if dotest_options.results_port is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001217 _remove_option(dotest_argv, "--results-port", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001218 if dotest_options.results_formatter is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001219 _remove_option(dotest_argv, "--results-formatter", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001220 if dotest_options.results_formatter_options is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001221 _remove_option(dotest_argv, "--results-formatter-option", "-O",
1222 True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001223
Todd Fiala33896a92015-09-18 21:01:13 +00001224 # Remove test runner name if present.
1225 if dotest_options.test_runner_name is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001226 _remove_option(dotest_argv, "--test-runner-name", None, True)
Todd Fiala33896a92015-09-18 21:01:13 +00001227
1228
Todd Fiala83c32e32015-09-22 21:19:40 +00001229def is_darwin_version_lower_than(target_version):
1230 """Checks that os is Darwin and version is lower than target_version.
1231
1232 @param target_version the StrictVersion indicating the version
1233 we're checking against.
1234
1235 @return True if the OS is Darwin (OS X) and the version number of
1236 the OS is less than target_version; False in all other cases.
1237 """
1238 if platform.system() != 'Darwin':
1239 # Can't be Darwin lower than a certain version.
1240 return False
1241
1242 system_version = distutils.version.StrictVersion(platform.mac_ver()[0])
Todd Fiala871b2e52015-09-22 22:47:34 +00001243 return cmp(system_version, target_version) < 0
Todd Fiala83c32e32015-09-22 21:19:40 +00001244
1245
1246def default_test_runner_name(num_threads):
1247 """Returns the default test runner name for the configuration.
1248
1249 @param num_threads the number of threads/workers this test runner is
1250 supposed to use.
1251
1252 @return the test runner name that should be used by default when
1253 no test runner was explicitly called out on the command line.
1254 """
1255 if num_threads == 1:
1256 # Use the serial runner.
1257 test_runner_name = "serial"
1258 elif os.name == "nt":
Adrian McCarthy040b31d2015-10-12 14:46:57 +00001259 # On Windows, Python uses CRT with a low limit on the number of open
1260 # files. If you have a lot of cores, the threading-pool runner will
1261 # often fail because it exceeds that limit.
1262 if num_threads > 32:
1263 test_runner_name = "multiprocessing-pool"
1264 else:
1265 test_runner_name = "threading-pool"
Todd Fiala83c32e32015-09-22 21:19:40 +00001266 elif is_darwin_version_lower_than(
1267 distutils.version.StrictVersion("10.10.0")):
1268 # OS X versions before 10.10 appear to have an issue using
1269 # the threading test runner. Fall back to multiprocessing.
1270 # Supports Ctrl-C.
1271 test_runner_name = "multiprocessing"
1272 else:
1273 # For everyone else, use the ctrl-c-enabled threading support.
1274 # Should use fewer system resources than the multprocessing
1275 # variant.
1276 test_runner_name = "threading"
1277 return test_runner_name
1278
1279
Todd Fiala8cbeed32015-09-08 22:22:33 +00001280def main(print_details_on_success, num_threads, test_subdir,
Todd Fiala68615ce2015-09-15 21:38:04 +00001281 test_runner_name, results_formatter):
Todd Fialafed95662015-09-03 18:58:44 +00001282 """Run dotest.py in inferior mode in parallel.
1283
1284 @param print_details_on_success the parsed value of the output-on-success
1285 command line argument. When True, details of a successful dotest inferior
1286 are printed even when everything succeeds. The normal behavior is to
1287 not print any details when all the inferior tests pass.
1288
1289 @param num_threads the parsed value of the num-threads command line
1290 argument.
1291
1292 @param test_subdir optionally specifies a subdir to limit testing
1293 within. May be None if the entire test tree is to be used. This subdir
1294 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001295
1296 @param test_runner_name if specified, contains the test runner
1297 name which selects the strategy used to run the isolated and
1298 optionally concurrent test runner. Specify None to allow the
1299 system to choose the most appropriate test runner given desired
1300 thread count and OS type.
1301
Todd Fiala68615ce2015-09-15 21:38:04 +00001302 @param results_formatter if specified, provides the TestResultsFormatter
1303 instance that will format and output test result data from the
1304 side-channel test results. When specified, inferior dotest calls
1305 will send test results side-channel data over a socket to the parallel
1306 test runner, which will forward them on to results_formatter.
Todd Fialafed95662015-09-03 18:58:44 +00001307 """
1308
Todd Fiala1cc97b42015-09-21 05:42:26 +00001309 # Do not shut down on sighup.
Ying Chend93aa102015-09-23 21:53:18 +00001310 if hasattr(signal, 'SIGHUP'):
1311 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala1cc97b42015-09-21 05:42:26 +00001312
Todd Fialafed95662015-09-03 18:58:44 +00001313 dotest_argv = sys.argv[1:]
1314
Greg Claytonb0d148e2015-09-21 17:25:01 +00001315 global output_on_success, RESULTS_FORMATTER
Todd Fialafed95662015-09-03 18:58:44 +00001316 output_on_success = print_details_on_success
Todd Fiala68615ce2015-09-15 21:38:04 +00001317 RESULTS_FORMATTER = results_formatter
Todd Fialafed95662015-09-03 18:58:44 +00001318
Vince Harrond5fa1022015-05-10 15:24:12 +00001319 # We can't use sys.path[0] to determine the script directory
1320 # because it doesn't work under a debugger
Vince Harron8994fed2015-05-22 19:49:23 +00001321 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +00001322 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +00001323 dotest_options = dotest_args.parse_args(parser, dotest_argv)
1324
Todd Fiala68615ce2015-09-15 21:38:04 +00001325 adjust_inferior_options(dotest_argv)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001326
1327 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +00001328
Vince Harrone06a7a82015-05-12 23:12:19 +00001329 # The root directory was specified on the command line
Todd Fiala68615ce2015-09-15 21:38:04 +00001330 test_directory = os.path.dirname(os.path.realpath(__file__))
Todd Fialafed95662015-09-03 18:58:44 +00001331 if test_subdir and len(test_subdir) > 0:
1332 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +00001333 else:
Todd Fialafed95662015-09-03 18:58:44 +00001334 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +00001335
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001336 # clean core files in test tree from previous runs (Linux)
1337 cores = find('core.*', test_subdir)
1338 for core in cores:
1339 os.unlink(core)
1340
Daniel Maleab42556f2013-04-19 18:32:53 +00001341 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +00001342
1343 # Figure out which testrunner strategy we'll use.
1344 runner_strategies_by_name = get_test_runner_strategies(num_threads)
1345
1346 # If the user didn't specify a test runner strategy, determine
1347 # the default now based on number of threads and OS type.
1348 if not test_runner_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001349 test_runner_name = default_test_runner_name(num_threads)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001350
1351 if test_runner_name not in runner_strategies_by_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001352 raise Exception(
1353 "specified testrunner name '{}' unknown. Valid choices: {}".format(
1354 test_runner_name,
1355 runner_strategies_by_name.keys()))
Todd Fiala8cbeed32015-09-08 22:22:33 +00001356 test_runner_func = runner_strategies_by_name[test_runner_name]
1357
1358 summary_results = walk_and_invoke(
Todd Fiala871b2e52015-09-22 22:47:34 +00001359 test_directory, test_subdir, dotest_argv,
1360 num_threads, test_runner_func)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001361
1362 (timed_out, passed, failed, unexpected_successes, pass_count,
1363 fail_count) = summary_results
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +00001364
Todd Fialade9a44e2015-09-22 00:15:50 +00001365 # The results formatter - if present - is done now. Tell it to
1366 # terminate.
1367 if results_formatter is not None:
1368 results_formatter.send_terminate_as_needed()
1369
Vince Harron17f429f2014-12-13 00:08:19 +00001370 timed_out = set(timed_out)
Chaoren Line80372a2015-08-12 18:02:53 +00001371 num_test_files = len(passed) + len(failed)
1372 num_test_cases = pass_count + fail_count
Daniel Maleab42556f2013-04-19 18:32:53 +00001373
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001374 # move core files into session dir
1375 cores = find('core.*', test_subdir)
1376 for core in cores:
1377 dst = core.replace(test_directory, "")[1:]
1378 dst = dst.replace(os.path.sep, "-")
1379 os.rename(core, os.path.join(session_dir, dst))
1380
Vince Harron06381732015-05-12 23:10:36 +00001381 # remove expected timeouts from failures
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001382 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
Vince Harron06381732015-05-12 23:10:36 +00001383 for xtime in expected_timeout:
1384 if xtime in timed_out:
1385 timed_out.remove(xtime)
1386 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001387 result = "ExpectedTimeout"
1388 elif xtime in passed:
1389 result = "UnexpectedCompletion"
1390 else:
1391 result = None # failed
1392
1393 if result:
1394 test_name = os.path.splitext(xtime)[0]
1395 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +00001396
Zachary Turnerff890da2015-10-19 23:45:41 +00001397 print()
Chaoren Lin5a59e462015-08-12 18:02:51 +00001398 sys.stdout.write("Ran %d test suites" % num_test_files)
1399 if num_test_files > 0:
1400 sys.stdout.write(" (%d failed) (%f%%)" % (
1401 len(failed), 100.0 * len(failed) / num_test_files))
Zachary Turnerff890da2015-10-19 23:45:41 +00001402 print()
Chaoren Line80372a2015-08-12 18:02:53 +00001403 sys.stdout.write("Ran %d test cases" % num_test_cases)
1404 if num_test_cases > 0:
Chaoren Lin5a59e462015-08-12 18:02:51 +00001405 sys.stdout.write(" (%d failed) (%f%%)" % (
Chaoren Line80372a2015-08-12 18:02:53 +00001406 fail_count, 100.0 * fail_count / num_test_cases))
Zachary Turnerff890da2015-10-19 23:45:41 +00001407 print()
Zachary Turner4cceca72015-08-14 16:45:32 +00001408 exit_code = 0
1409
Daniel Maleacbaef262013-02-15 21:31:37 +00001410 if len(failed) > 0:
Shawn Best13491c42014-10-22 19:29:00 +00001411 failed.sort()
Zachary Turnerff890da2015-10-19 23:45:41 +00001412 print("Failing Tests (%d)" % len(failed))
Daniel Maleacbaef262013-02-15 21:31:37 +00001413 for f in failed:
Zachary Turnerff890da2015-10-19 23:45:41 +00001414 print("%s: LLDB (suite) :: %s (%s)" % (
Vince Harron17f429f2014-12-13 00:08:19 +00001415 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
Zachary Turnerff890da2015-10-19 23:45:41 +00001416 ))
Zachary Turner4cceca72015-08-14 16:45:32 +00001417 exit_code = 1
1418
1419 if len(unexpected_successes) > 0:
1420 unexpected_successes.sort()
Zachary Turnerff890da2015-10-19 23:45:41 +00001421 print("\nUnexpected Successes (%d)" % len(unexpected_successes))
Zachary Turner4cceca72015-08-14 16:45:32 +00001422 for u in unexpected_successes:
Zachary Turnerff890da2015-10-19 23:45:41 +00001423 print("UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info))
Zachary Turner4cceca72015-08-14 16:45:32 +00001424
1425 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001426
1427if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +00001428 sys.stderr.write(
1429 "error: dosep.py no longer supports being called directly. "
1430 "Please call dotest.py directly. The dosep.py-specific arguments "
1431 "have been added under the Parallel processing arguments.\n")
1432 sys.exit(128)