blob: f3fd3ea6ccbb866a91aecac9d0597de3a2411ce7 [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
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000036from __future__ import absolute_import
Zachary Turner814236d2015-10-21 17:48:52 +000037
Todd Fiala2d3754d2015-09-29 22:19:06 +000038# system packages and modules
Todd Fiala68615ce2015-09-15 21:38:04 +000039import asyncore
Todd Fiala83c32e32015-09-22 21:19:40 +000040import distutils.version
Vince Harrondcc2b9f2015-05-27 04:40:36 +000041import fnmatch
Todd Fiala8cbeed32015-09-08 22:22:33 +000042import multiprocessing
43import multiprocessing.pool
44import os
Todd Fiala3f0a3602014-07-08 06:42:37 +000045import platform
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
Zachary Turner814236d2015-10-21 17:48:52 +000051from six.moves import queue
52
Todd Fiala2d3754d2015-09-29 22:19:06 +000053# Our packages and modules
Zachary Turnerc1b7cd72015-11-05 19:22:28 +000054import lldbsuite.support.seven as seven
55
56from . import dotest_channels
57from . import dotest_args
58
59# Todo: Convert this folder layout to be relative-import friendly and don't hack up
60# sys.path like this
61sys.path.append(os.path.join(os.path.dirname(__file__), "test_runner", "lib"))
Todd Fiala2d3754d2015-09-29 22:19:06 +000062import lldb_utils
63import process_control
Vince Harron17f429f2014-12-13 00:08:19 +000064
Vince Harron17f429f2014-12-13 00:08:19 +000065# Status codes for running command with timeout.
66eTimedOut, ePassed, eFailed = 124, 0, 1
67
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000068output_lock = None
69test_counter = None
70total_tests = None
Chaoren Linffc63b02015-08-12 18:02:49 +000071test_name_len = None
Pavel Labath05ab2372015-07-06 15:57:52 +000072dotest_options = None
Zachary Turner38e64172015-08-10 17:46:11 +000073output_on_success = False
Todd Fiala68615ce2015-09-15 21:38:04 +000074RESULTS_FORMATTER = None
75RUNNER_PROCESS_ASYNC_MAP = None
76RESULTS_LISTENER_CHANNEL = None
Chaoren Linb6325d02015-08-12 18:02:54 +000077
Todd Fiala33896a92015-09-18 21:01:13 +000078"""Contains an optional function pointer that can return the worker index
79 for the given thread/process calling it. Returns a 0-based index."""
80GET_WORKER_INDEX = None
81
Todd Fiala1cc97b42015-09-21 05:42:26 +000082
Todd Fiala33896a92015-09-18 21:01:13 +000083def setup_global_variables(
84 lock, counter, total, name_len, options, worker_index_map):
Chaoren Linffc63b02015-08-12 18:02:49 +000085 global output_lock, test_counter, total_tests, test_name_len
86 global dotest_options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000087 output_lock = lock
88 test_counter = counter
89 total_tests = total
Chaoren Linffc63b02015-08-12 18:02:49 +000090 test_name_len = name_len
Pavel Labath05ab2372015-07-06 15:57:52 +000091 dotest_options = options
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +000092
Todd Fiala33896a92015-09-18 21:01:13 +000093 if worker_index_map is not None:
94 # We'll use the output lock for this to avoid sharing another lock.
95 # This won't be used much.
96 index_lock = lock
97
98 def get_worker_index_use_pid():
99 """Returns a 0-based, process-unique index for the worker."""
100 pid = os.getpid()
101 with index_lock:
102 if pid not in worker_index_map:
103 worker_index_map[pid] = len(worker_index_map)
104 return worker_index_map[pid]
105
106 global GET_WORKER_INDEX
107 GET_WORKER_INDEX = get_worker_index_use_pid
108
Chaoren Linb6325d02015-08-12 18:02:54 +0000109
Zachary Turner38e64172015-08-10 17:46:11 +0000110def report_test_failure(name, command, output):
111 global output_lock
112 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000113 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
Zachary Turnerff890da2015-10-19 23:45:41 +0000114 print(file=sys.stderr)
115 print(output, file=sys.stderr)
116 print("[%s FAILED]" % name, file=sys.stderr)
117 print("Command invoked: %s" % ' '.join(command), file=sys.stderr)
Chaoren Linffc63b02015-08-12 18:02:49 +0000118 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000119
Chaoren Linb6325d02015-08-12 18:02:54 +0000120
Zachary Turner38e64172015-08-10 17:46:11 +0000121def report_test_pass(name, output):
122 global output_lock, output_on_success
123 with output_lock:
Greg Clayton1827fc22015-09-19 00:39:09 +0000124 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
125 if output_on_success:
Zachary Turnerff890da2015-10-19 23:45:41 +0000126 print(file=sys.stderr)
127 print(output, file=sys.stderr)
128 print("[%s PASSED]" % name, file=sys.stderr)
Chaoren Linffc63b02015-08-12 18:02:49 +0000129 update_progress(name)
Zachary Turner38e64172015-08-10 17:46:11 +0000130
Chaoren Linb6325d02015-08-12 18:02:54 +0000131
Chaoren Linffc63b02015-08-12 18:02:49 +0000132def update_progress(test_name=""):
133 global output_lock, test_counter, total_tests, test_name_len
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000134 with output_lock:
Chaoren Linffc63b02015-08-12 18:02:49 +0000135 counter_len = len(str(total_tests))
Greg Clayton1827fc22015-09-19 00:39:09 +0000136 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
137 sys.stderr.write(
138 "\r%*d out of %d test suites processed - %-*s" %
139 (counter_len, test_counter.value, total_tests,
140 test_name_len.value, test_name))
Chaoren Linffc63b02015-08-12 18:02:49 +0000141 if len(test_name) > test_name_len.value:
142 test_name_len.value = len(test_name)
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000143 test_counter.value += 1
Zachary Turner38e64172015-08-10 17:46:11 +0000144 sys.stdout.flush()
145 sys.stderr.flush()
Chaoren Lin5e3ab2b2015-06-01 17:49:25 +0000146
Chaoren Linb6325d02015-08-12 18:02:54 +0000147
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000148def parse_test_results(output):
149 passes = 0
150 failures = 0
Zachary Turner4cceca72015-08-14 16:45:32 +0000151 unexpected_successes = 0
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000152 for result in output:
Chaoren Linb6325d02015-08-12 18:02:54 +0000153 pass_count = re.search("^RESULT:.*([0-9]+) passes",
154 result, re.MULTILINE)
155 fail_count = re.search("^RESULT:.*([0-9]+) failures",
156 result, re.MULTILINE)
157 error_count = re.search("^RESULT:.*([0-9]+) errors",
158 result, re.MULTILINE)
Zachary Turner4cceca72015-08-14 16:45:32 +0000159 unexpected_success_count = re.search("^RESULT:.*([0-9]+) unexpected successes",
160 result, re.MULTILINE)
Chaoren Linb6325d02015-08-12 18:02:54 +0000161 if pass_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000162 passes = passes + int(pass_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000163 if fail_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000164 failures = failures + int(fail_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000165 if unexpected_success_count is not None:
166 unexpected_successes = unexpected_successes + int(unexpected_success_count.group(1))
Chaoren Linb6325d02015-08-12 18:02:54 +0000167 if error_count is not None:
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000168 failures = failures + int(error_count.group(1))
Zachary Turner4cceca72015-08-14 16:45:32 +0000169 return passes, failures, unexpected_successes
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000170
Chaoren Linb6325d02015-08-12 18:02:54 +0000171
Todd Fiala2d3754d2015-09-29 22:19:06 +0000172class DoTestProcessDriver(process_control.ProcessDriver):
173 """Drives the dotest.py inferior process and handles bookkeeping."""
174 def __init__(self, output_file, output_file_lock, pid_events, file_name,
175 soft_terminate_timeout):
176 super(DoTestProcessDriver, self).__init__(
177 soft_terminate_timeout=soft_terminate_timeout)
178 self.output_file = output_file
179 self.output_lock = lldb_utils.OptionalWith(output_file_lock)
180 self.pid_events = pid_events
181 self.results = None
182 self.file_name = file_name
183
184 def write(self, content):
185 with self.output_lock:
186 self.output_file.write(content)
187
188 def on_process_started(self):
189 if self.pid_events:
190 self.pid_events.put_nowait(('created', self.process.pid))
191
192 def on_process_exited(self, command, output, was_timeout, exit_status):
193 if self.pid_events:
194 # No point in culling out those with no exit_status (i.e.
195 # those we failed to kill). That would just cause
196 # downstream code to try to kill it later on a Ctrl-C. At
197 # this point, a best-effort-to-kill already took place. So
198 # call it destroyed here.
199 self.pid_events.put_nowait(('destroyed', self.process.pid))
200
201 # Override the exit status if it was a timeout.
202 if was_timeout:
203 exit_status = eTimedOut
204
205 # If we didn't end up with any output, call it empty for
206 # stdout/stderr.
207 if output is None:
208 output = ('', '')
209
210 # Now parse the output.
211 passes, failures, unexpected_successes = parse_test_results(output)
212 if exit_status == 0:
213 # stdout does not have any useful information from 'dotest.py',
214 # only stderr does.
215 report_test_pass(self.file_name, output[1])
216 else:
217 report_test_failure(self.file_name, command, output[1])
218
219 # Save off the results for the caller.
220 self.results = (
221 self.file_name,
222 exit_status,
223 passes,
224 failures,
225 unexpected_successes)
226
227
228def get_soft_terminate_timeout():
229 # Defaults to 10 seconds, but can set
230 # LLDB_TEST_SOFT_TERMINATE_TIMEOUT to a floating point
231 # number in seconds. This value indicates how long
232 # the test runner will wait for the dotest inferior to
233 # handle a timeout via a soft terminate before it will
234 # assume that failed and do a hard terminate.
235
236 # TODO plumb through command-line option
237 return float(os.environ.get('LLDB_TEST_SOFT_TERMINATE_TIMEOUT', 10.0))
238
239
240def want_core_on_soft_terminate():
241 # TODO plumb through command-line option
242 if platform.system() == 'Linux':
243 return True
244 else:
245 return False
Todd Fialada817b62015-09-22 18:05:11 +0000246
247
Todd Fiala8cbeed32015-09-08 22:22:33 +0000248def call_with_timeout(command, timeout, name, inferior_pid_events):
Todd Fiala2d3754d2015-09-29 22:19:06 +0000249 # Add our worker index (if we have one) to all test events
250 # from this inferior.
Todd Fiala33896a92015-09-18 21:01:13 +0000251 if GET_WORKER_INDEX is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000252 try:
253 worker_index = GET_WORKER_INDEX()
254 command.extend([
Todd Fiala2d3754d2015-09-29 22:19:06 +0000255 "--event-add-entries",
256 "worker_index={}:int".format(worker_index)])
257 except: # pylint: disable=bare-except
258 # Ctrl-C does bad things to multiprocessing.Manager.dict()
259 # lookup. Just swallow it.
Todd Fialae83f1402015-09-18 22:45:31 +0000260 pass
261
Todd Fiala2d3754d2015-09-29 22:19:06 +0000262 # Create the inferior dotest.py ProcessDriver.
263 soft_terminate_timeout = get_soft_terminate_timeout()
264 want_core = want_core_on_soft_terminate()
Todd Fiala68615ce2015-09-15 21:38:04 +0000265
Todd Fiala2d3754d2015-09-29 22:19:06 +0000266 process_driver = DoTestProcessDriver(
267 sys.stdout,
268 output_lock,
269 inferior_pid_events,
270 name,
271 soft_terminate_timeout)
Todd Fiala68615ce2015-09-15 21:38:04 +0000272
Todd Fiala2d3754d2015-09-29 22:19:06 +0000273 # Run it with a timeout.
274 process_driver.run_command_with_timeout(command, timeout, want_core)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000275
Todd Fiala2d3754d2015-09-29 22:19:06 +0000276 # Return the results.
277 if not process_driver.results:
278 # This is truly exceptional. Even a failing or timed out
279 # binary should have called the results-generation code.
280 raise Exception("no test results were generated whatsoever")
281 return process_driver.results
Johnny Chene8d9dc62011-10-31 19:04:07 +0000282
Chaoren Linb6325d02015-08-12 18:02:54 +0000283
Zachary Turner7d564542015-11-02 19:19:49 +0000284def process_dir(root, files, dotest_argv, inferior_pid_events):
Steve Puccibefe2b12014-03-07 00:01:11 +0000285 """Examine a directory for tests, and invoke any found within it."""
Chaoren Line80372a2015-08-12 18:02:53 +0000286 results = []
Steve Puccibefe2b12014-03-07 00:01:11 +0000287 for name in files:
Zachary Turner7d564542015-11-02 19:19:49 +0000288 import __main__ as main
289 script_file = main.__file__
Zachary Turnerf6896b02015-01-05 19:37:03 +0000290 command = ([sys.executable, script_file] +
Vince Harron41657cc2015-05-21 18:15:09 +0000291 dotest_argv +
Todd Fialafed95662015-09-03 18:58:44 +0000292 ["--inferior", "-p", name, root])
Vince Harron17f429f2014-12-13 00:08:19 +0000293
294 timeout_name = os.path.basename(os.path.splitext(name)[0]).upper()
295
Chaoren Linb6325d02015-08-12 18:02:54 +0000296 timeout = (os.getenv("LLDB_%s_TIMEOUT" % timeout_name) or
297 getDefaultTimeout(dotest_options.lldb_platform_name))
Vince Harron17f429f2014-12-13 00:08:19 +0000298
Todd Fiala8cbeed32015-09-08 22:22:33 +0000299 results.append(call_with_timeout(
300 command, timeout, name, inferior_pid_events))
Vince Harron17f429f2014-12-13 00:08:19 +0000301
Zachary Turner4cceca72015-08-14 16:45:32 +0000302 # result = (name, status, passes, failures, unexpected_successes)
303 timed_out = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000304 if status == eTimedOut]
Zachary Turner4cceca72015-08-14 16:45:32 +0000305 passed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000306 if status == ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000307 failed = [name for name, status, _, _, _ in results
Chaoren Line80372a2015-08-12 18:02:53 +0000308 if status != ePassed]
Zachary Turner4cceca72015-08-14 16:45:32 +0000309 unexpected_passes = [name for name, _, _, _, unexpected_successes in results
310 if unexpected_successes > 0]
Todd Fialafed95662015-09-03 18:58:44 +0000311
Chaoren Line80372a2015-08-12 18:02:53 +0000312 pass_count = sum([result[2] for result in results])
313 fail_count = sum([result[3] for result in results])
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +0000314
Zachary Turner4cceca72015-08-14 16:45:32 +0000315 return (timed_out, passed, failed, unexpected_passes, pass_count, fail_count)
Steve Puccibefe2b12014-03-07 00:01:11 +0000316
317in_q = None
318out_q = None
319
Chaoren Linb6325d02015-08-12 18:02:54 +0000320
Todd Fiala8cbeed32015-09-08 22:22:33 +0000321def process_dir_worker_multiprocessing(
322 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000323 a_dotest_options, job_queue, result_queue, inferior_pid_events,
324 worker_index_map):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000325 """Worker thread main loop when in multiprocessing mode.
Steve Puccibefe2b12014-03-07 00:01:11 +0000326 Takes one directory specification at a time and works on it."""
Todd Fiala8cbeed32015-09-08 22:22:33 +0000327
328 # Shut off interrupt handling in the child process.
329 signal.signal(signal.SIGINT, signal.SIG_IGN)
Ying Chend93aa102015-09-23 21:53:18 +0000330 if hasattr(signal, 'SIGHUP'):
331 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000332
333 # Setup the global state for the worker process.
334 setup_global_variables(
335 a_output_lock, a_test_counter, a_total_tests, a_test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000336 a_dotest_options, worker_index_map)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000337
338 # Keep grabbing entries from the queue until done.
339 while not job_queue.empty():
340 try:
341 job = job_queue.get(block=False)
Pavel Labath48c6b522015-11-02 20:54:25 +0000342 result = process_dir(job[0], job[1], job[2],
Todd Fiala8cbeed32015-09-08 22:22:33 +0000343 inferior_pid_events)
344 result_queue.put(result)
Zachary Turner814236d2015-10-21 17:48:52 +0000345 except queue.Empty:
Todd Fiala8cbeed32015-09-08 22:22:33 +0000346 # Fine, we're done.
347 pass
Steve Puccibefe2b12014-03-07 00:01:11 +0000348
Chaoren Linb6325d02015-08-12 18:02:54 +0000349
Todd Fiala8cbeed32015-09-08 22:22:33 +0000350def process_dir_worker_multiprocessing_pool(args):
351 return process_dir(*args)
352
353
Todd Fiala68615ce2015-09-15 21:38:04 +0000354def process_dir_worker_threading(job_queue, result_queue, inferior_pid_events):
Todd Fiala8cbeed32015-09-08 22:22:33 +0000355 """Worker thread main loop when in threading mode.
356
357 This one supports the hand-rolled pooling support.
358
359 Takes one directory specification at a time and works on it."""
360
361 # Keep grabbing entries from the queue until done.
362 while not job_queue.empty():
363 try:
364 job = job_queue.get(block=False)
Pavel Labath48c6b522015-11-02 20:54:25 +0000365 result = process_dir(job[0], job[1], job[2],
Todd Fiala8cbeed32015-09-08 22:22:33 +0000366 inferior_pid_events)
367 result_queue.put(result)
Zachary Turner814236d2015-10-21 17:48:52 +0000368 except queue.Empty:
Todd Fiala8cbeed32015-09-08 22:22:33 +0000369 # Fine, we're done.
370 pass
371
372
373def process_dir_worker_threading_pool(args):
374 return process_dir(*args)
375
376
377def process_dir_mapper_inprocess(args):
378 """Map adapter for running the subprocess-based, non-threaded test runner.
379
380 @param args the process work item tuple
381 @return the test result tuple
382 """
383 return process_dir(*args)
384
385
386def collect_active_pids_from_pid_events(event_queue):
387 """
388 Returns the set of what should be active inferior pids based on
389 the event stream.
390
391 @param event_queue a multiprocessing.Queue containing events of the
392 form:
393 ('created', pid)
394 ('destroyed', pid)
395
396 @return set of inferior dotest.py pids activated but never completed.
397 """
398 active_pid_set = set()
399 while not event_queue.empty():
400 pid_event = event_queue.get_nowait()
401 if pid_event[0] == 'created':
402 active_pid_set.add(pid_event[1])
403 elif pid_event[0] == 'destroyed':
404 active_pid_set.remove(pid_event[1])
405 return active_pid_set
406
407
408def kill_all_worker_processes(workers, inferior_pid_events):
409 """
410 Kills all specified worker processes and their process tree.
411
412 @param workers a list of multiprocess.Process worker objects.
413 @param inferior_pid_events a multiprocess.Queue that contains
414 all inferior create and destroy events. Used to construct
415 the list of child pids still outstanding that need to be killed.
416 """
417 for worker in workers:
418 worker.terminate()
419 worker.join()
420
421 # Add all the child test pids created.
422 active_pid_set = collect_active_pids_from_pid_events(
423 inferior_pid_events)
424 for inferior_pid in active_pid_set:
Zachary Turnerff890da2015-10-19 23:45:41 +0000425 print("killing inferior pid {}".format(inferior_pid))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000426 os.kill(inferior_pid, signal.SIGKILL)
427
428
429def kill_all_worker_threads(workers, inferior_pid_events):
430 """
431 Kills all specified worker threads and their process tree.
432
433 @param workers a list of multiprocess.Process worker objects.
434 @param inferior_pid_events a multiprocess.Queue that contains
435 all inferior create and destroy events. Used to construct
436 the list of child pids still outstanding that need to be killed.
437 """
438
439 # Add all the child test pids created.
440 active_pid_set = collect_active_pids_from_pid_events(
441 inferior_pid_events)
442 for inferior_pid in active_pid_set:
Zachary Turnerff890da2015-10-19 23:45:41 +0000443 print("killing inferior pid {}".format(inferior_pid))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000444 os.kill(inferior_pid, signal.SIGKILL)
445
446 # We don't have a way to nuke the threads. However, since we killed
447 # all the inferiors, and we drained the job queue, this will be
448 # good enough. Wait cleanly for each worker thread to wrap up.
449 for worker in workers:
450 worker.join()
451
452
453def find_test_files_in_dir_tree(dir_root, found_func):
454 """Calls found_func for all the test files in the given dir hierarchy.
455
456 @param dir_root the path to the directory to start scanning
457 for test files. All files in this directory and all its children
458 directory trees will be searched.
459
460 @param found_func a callable object that will be passed
461 the parent directory (relative to dir_root) and the list of
462 test files from within that directory.
463 """
464 for root, _, files in os.walk(dir_root, topdown=False):
465 def is_test_filename(test_dir, base_filename):
466 """Returns True if the given filename matches the test name format.
467
468 @param test_dir the directory to check. Should be absolute or
469 relative to current working directory.
470
471 @param base_filename the base name of the filename to check for a
472 dherence to the python test case filename format.
473
474 @return True if name matches the python test case filename format.
475 """
476 # Not interested in symbolically linked files.
477 if os.path.islink(os.path.join(test_dir, base_filename)):
478 return False
479 # Only interested in test files with the "Test*.py" naming pattern.
480 return (base_filename.startswith("Test") and
481 base_filename.endswith(".py"))
482
483 tests = [filename for filename in files
484 if is_test_filename(root, filename)]
485 if tests:
486 found_func(root, tests)
487
488
489def initialize_global_vars_common(num_threads, test_work_items):
490 global total_tests, test_counter, test_name_len
Greg Clayton1827fc22015-09-19 00:39:09 +0000491
Todd Fiala8cbeed32015-09-08 22:22:33 +0000492 total_tests = sum([len(item[1]) for item in test_work_items])
493 test_counter = multiprocessing.Value('i', 0)
494 test_name_len = multiprocessing.Value('i', 0)
Greg Clayton1827fc22015-09-19 00:39:09 +0000495 if not (RESULTS_FORMATTER and RESULTS_FORMATTER.is_using_terminal()):
Zachary Turnerff890da2015-10-19 23:45:41 +0000496 print("Testing: %d test suites, %d thread%s" % (
497 total_tests, num_threads, (num_threads > 1) * "s"), file=sys.stderr)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000498 update_progress()
499
500
501def initialize_global_vars_multiprocessing(num_threads, test_work_items):
502 # Initialize the global state we'll use to communicate with the
503 # rest of the flat module.
504 global output_lock
505 output_lock = multiprocessing.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000506
Todd Fiala8cbeed32015-09-08 22:22:33 +0000507 initialize_global_vars_common(num_threads, test_work_items)
508
509
510def initialize_global_vars_threading(num_threads, test_work_items):
Todd Fiala33896a92015-09-18 21:01:13 +0000511 """Initializes global variables used in threading mode.
512 @param num_threads specifies the number of workers used.
513 @param test_work_items specifies all the work items
514 that will be processed.
515 """
Todd Fiala8cbeed32015-09-08 22:22:33 +0000516 # Initialize the global state we'll use to communicate with the
517 # rest of the flat module.
518 global output_lock
519 output_lock = threading.RLock()
Todd Fiala33896a92015-09-18 21:01:13 +0000520
521 index_lock = threading.RLock()
522 index_map = {}
523
524 def get_worker_index_threading():
525 """Returns a 0-based, thread-unique index for the worker thread."""
526 thread_id = threading.current_thread().ident
527 with index_lock:
528 if thread_id not in index_map:
529 index_map[thread_id] = len(index_map)
530 return index_map[thread_id]
531
532
533 global GET_WORKER_INDEX
534 GET_WORKER_INDEX = get_worker_index_threading
535
Todd Fiala8cbeed32015-09-08 22:22:33 +0000536 initialize_global_vars_common(num_threads, test_work_items)
537
538
Todd Fiala68615ce2015-09-15 21:38:04 +0000539def ctrl_c_loop(main_op_func, done_func, ctrl_c_handler):
540 """Provides a main loop that is Ctrl-C protected.
541
542 The main loop calls the main_op_func() repeatedly until done_func()
543 returns true. The ctrl_c_handler() method is called with a single
544 int parameter that contains the number of times the ctrl_c has been
545 hit (starting with 1). The ctrl_c_handler() should mutate whatever
546 it needs to have the done_func() return True as soon as it is desired
547 to exit the loop.
548 """
549 done = False
550 ctrl_c_count = 0
551
552 while not done:
553 try:
554 # See if we're done. Start with done check since it is
555 # the first thing executed after a Ctrl-C handler in the
556 # following loop.
557 done = done_func()
558 if not done:
559 # Run the main op once.
560 main_op_func()
561
562 except KeyboardInterrupt:
563 ctrl_c_count += 1
564 ctrl_c_handler(ctrl_c_count)
565
566
567def pump_workers_and_asyncore_map(workers, asyncore_map):
568 """Prunes out completed workers and maintains the asyncore loop.
569
570 The asyncore loop contains the optional socket listener
571 and handlers. When all workers are complete, this method
572 takes care of stopping the listener. It also runs the
573 asyncore loop for the given async map for 10 iterations.
574
575 @param workers the list of worker Thread/Process instances.
576
577 @param asyncore_map the asyncore threading-aware map that
578 indicates which channels are in use and still alive.
579 """
580
581 # Check on all the workers, removing them from the workers
582 # list as they complete.
583 dead_workers = []
584 for worker in workers:
585 # This non-blocking join call is what allows us
586 # to still receive keyboard interrupts.
587 worker.join(0.01)
588 if not worker.is_alive():
589 dead_workers.append(worker)
590 # Clear out the completed workers
591 for dead_worker in dead_workers:
592 workers.remove(dead_worker)
593
594 # If there are no more workers and there is a listener,
595 # close the listener.
596 global RESULTS_LISTENER_CHANNEL
597 if len(workers) == 0 and RESULTS_LISTENER_CHANNEL is not None:
598 RESULTS_LISTENER_CHANNEL.close()
599 RESULTS_LISTENER_CHANNEL = None
600
601 # Pump the asyncore map if it isn't empty.
602 if len(asyncore_map) > 0:
603 asyncore.loop(0.1, False, asyncore_map, 10)
604
605
606def handle_ctrl_c(ctrl_c_count, job_queue, workers, inferior_pid_events,
607 stop_all_inferiors_func):
608 """Performs the appropriate ctrl-c action for non-pool parallel test runners
609
610 @param ctrl_c_count starting with 1, indicates the number of times ctrl-c
611 has been intercepted. The value is 1 on the first intercept, 2 on the
612 second, etc.
613
614 @param job_queue a Queue object that contains the work still outstanding
615 (i.e. hasn't been assigned to a worker yet).
616
617 @param workers list of Thread or Process workers.
618
619 @param inferior_pid_events specifies a Queue of inferior process
620 construction and destruction events. Used to build the list of inferior
621 processes that should be killed if we get that far.
622
623 @param stop_all_inferiors_func a callable object that takes the
624 workers and inferior_pid_events parameters (in that order) if a hard
625 stop is to be used on the workers.
626 """
627
628 # Print out which Ctrl-C we're handling.
629 key_name = [
630 "first",
631 "second",
632 "third",
633 "many"]
634
635 if ctrl_c_count < len(key_name):
636 name_index = ctrl_c_count - 1
637 else:
638 name_index = len(key_name) - 1
639 message = "\nHandling {} KeyboardInterrupt".format(key_name[name_index])
640 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000641 print(message)
Todd Fiala68615ce2015-09-15 21:38:04 +0000642
643 if ctrl_c_count == 1:
644 # Remove all outstanding items from the work queue so we stop
645 # doing any more new work.
646 while not job_queue.empty():
647 try:
648 # Just drain it to stop more work from being started.
649 job_queue.get_nowait()
Zachary Turner814236d2015-10-21 17:48:52 +0000650 except queue.Empty:
Todd Fiala68615ce2015-09-15 21:38:04 +0000651 pass
652 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000653 print("Stopped more work from being started.")
Todd Fiala68615ce2015-09-15 21:38:04 +0000654 elif ctrl_c_count == 2:
655 # Try to stop all inferiors, even the ones currently doing work.
656 stop_all_inferiors_func(workers, inferior_pid_events)
657 else:
658 with output_lock:
Zachary Turnerff890da2015-10-19 23:45:41 +0000659 print("All teardown activities kicked off, should finish soon.")
Todd Fiala68615ce2015-09-15 21:38:04 +0000660
661
662def workers_and_async_done(workers, async_map):
663 """Returns True if the workers list and asyncore channels are all done.
664
665 @param workers list of workers (threads/processes). These must adhere
666 to the threading Thread or multiprocessing.Process interface.
667
668 @param async_map the threading-aware asyncore channel map to check
669 for live channels.
670
671 @return False if the workers list exists and has any entries in it, or
672 if the async_map exists and has any entries left in it; otherwise, True.
673 """
674 if workers is not None and len(workers) > 0:
675 # We're not done if we still have workers left.
676 return False
677 if async_map is not None and len(async_map) > 0:
678 return False
679 # We're done.
680 return True
681
682
Todd Fiala8cbeed32015-09-08 22:22:33 +0000683def multiprocessing_test_runner(num_threads, test_work_items):
684 """Provides hand-wrapped pooling test runner adapter with Ctrl-C support.
685
686 This concurrent test runner is based on the multiprocessing
687 library, and rolls its own worker pooling strategy so it
688 can handle Ctrl-C properly.
689
690 This test runner is known to have an issue running on
691 Windows platforms.
692
693 @param num_threads the number of worker processes to use.
694
695 @param test_work_items the iterable of test work item tuples
696 to run.
697 """
698
699 # Initialize our global state.
700 initialize_global_vars_multiprocessing(num_threads, test_work_items)
701
702 # Create jobs.
703 job_queue = multiprocessing.Queue(len(test_work_items))
704 for test_work_item in test_work_items:
705 job_queue.put(test_work_item)
706
707 result_queue = multiprocessing.Queue(len(test_work_items))
708
709 # Create queues for started child pids. Terminating
710 # the multiprocess processes does not terminate the
711 # child processes they spawn. We can remove this tracking
712 # if/when we move to having the multiprocess process directly
713 # perform the test logic. The Queue size needs to be able to
714 # hold 2 * (num inferior dotest.py processes started) entries.
715 inferior_pid_events = multiprocessing.Queue(4096)
716
Todd Fiala33896a92015-09-18 21:01:13 +0000717 # Worker dictionary allows each worker to figure out its worker index.
718 manager = multiprocessing.Manager()
719 worker_index_map = manager.dict()
720
Todd Fiala8cbeed32015-09-08 22:22:33 +0000721 # Create workers. We don't use multiprocessing.Pool due to
722 # challenges with handling ^C keyboard interrupts.
723 workers = []
724 for _ in range(num_threads):
725 worker = multiprocessing.Process(
726 target=process_dir_worker_multiprocessing,
727 args=(output_lock,
728 test_counter,
729 total_tests,
730 test_name_len,
731 dotest_options,
732 job_queue,
733 result_queue,
Todd Fiala33896a92015-09-18 21:01:13 +0000734 inferior_pid_events,
735 worker_index_map))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000736 worker.start()
737 workers.append(worker)
738
Todd Fiala68615ce2015-09-15 21:38:04 +0000739 # Main loop: wait for all workers to finish and wait for
740 # the socket handlers to wrap up.
741 ctrl_c_loop(
742 # Main operation of loop
743 lambda: pump_workers_and_asyncore_map(
744 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000745
Todd Fiala68615ce2015-09-15 21:38:04 +0000746 # Return True when we're done with the main loop.
747 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000748
Todd Fiala68615ce2015-09-15 21:38:04 +0000749 # Indicate what we do when we receive one or more Ctrl-Cs.
750 lambda ctrl_c_count: handle_ctrl_c(
751 ctrl_c_count, job_queue, workers, inferior_pid_events,
752 kill_all_worker_processes))
753
754 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000755 test_results = []
756 while not result_queue.empty():
757 test_results.append(result_queue.get(block=False))
758 return test_results
759
760
Todd Fiala68615ce2015-09-15 21:38:04 +0000761def map_async_run_loop(future, channel_map, listener_channel):
762 """Blocks until the Pool.map_async completes and the channel completes.
763
764 @param future an AsyncResult instance from a Pool.map_async() call.
765
766 @param channel_map the asyncore dispatch channel map that should be pumped.
767 Optional: may be None.
768
769 @param listener_channel the channel representing a listener that should be
770 closed once the map_async results are available.
771
772 @return the results from the async_result instance.
773 """
774 map_results = None
775
776 done = False
777 while not done:
778 # Check if we need to reap the map results.
779 if map_results is None:
780 if future.ready():
781 # Get the results.
782 map_results = future.get()
783
784 # Close the runner process listener channel if we have
785 # one: no more connections will be incoming.
786 if listener_channel is not None:
787 listener_channel.close()
788
789 # Pump the asyncore loop if we have a listener socket.
790 if channel_map is not None:
791 asyncore.loop(0.01, False, channel_map, 10)
792
793 # Figure out if we're done running.
794 done = map_results is not None
795 if channel_map is not None:
796 # We have a runner process async map. Check if it
797 # is complete.
798 if len(channel_map) > 0:
799 # We still have an asyncore channel running. Not done yet.
800 done = False
801
802 return map_results
803
804
Todd Fiala8cbeed32015-09-08 22:22:33 +0000805def multiprocessing_test_runner_pool(num_threads, test_work_items):
806 # Initialize our global state.
807 initialize_global_vars_multiprocessing(num_threads, test_work_items)
808
Todd Fiala33896a92015-09-18 21:01:13 +0000809 manager = multiprocessing.Manager()
810 worker_index_map = manager.dict()
811
Todd Fiala8cbeed32015-09-08 22:22:33 +0000812 pool = multiprocessing.Pool(
813 num_threads,
814 initializer=setup_global_variables,
815 initargs=(output_lock, test_counter, total_tests, test_name_len,
Todd Fiala33896a92015-09-18 21:01:13 +0000816 dotest_options, worker_index_map))
Todd Fiala68615ce2015-09-15 21:38:04 +0000817
818 # Start the map operation (async mode).
819 map_future = pool.map_async(
820 process_dir_worker_multiprocessing_pool, test_work_items)
821 return map_async_run_loop(
822 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000823
824
825def threading_test_runner(num_threads, test_work_items):
826 """Provides hand-wrapped pooling threading-based test runner adapter
827 with Ctrl-C support.
828
829 This concurrent test runner is based on the threading
830 library, and rolls its own worker pooling strategy so it
831 can handle Ctrl-C properly.
832
833 @param num_threads the number of worker processes to use.
834
835 @param test_work_items the iterable of test work item tuples
836 to run.
837 """
838
839 # Initialize our global state.
840 initialize_global_vars_threading(num_threads, test_work_items)
841
842 # Create jobs.
Zachary Turner814236d2015-10-21 17:48:52 +0000843 job_queue = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000844 for test_work_item in test_work_items:
845 job_queue.put(test_work_item)
846
Zachary Turner814236d2015-10-21 17:48:52 +0000847 result_queue = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000848
849 # Create queues for started child pids. Terminating
850 # the threading threads does not terminate the
851 # child processes they spawn.
Zachary Turner814236d2015-10-21 17:48:52 +0000852 inferior_pid_events = queue.Queue()
Todd Fiala8cbeed32015-09-08 22:22:33 +0000853
854 # Create workers. We don't use multiprocessing.pool.ThreadedPool
855 # due to challenges with handling ^C keyboard interrupts.
856 workers = []
857 for _ in range(num_threads):
858 worker = threading.Thread(
859 target=process_dir_worker_threading,
Todd Fiala68615ce2015-09-15 21:38:04 +0000860 args=(job_queue,
Todd Fiala8cbeed32015-09-08 22:22:33 +0000861 result_queue,
862 inferior_pid_events))
863 worker.start()
864 workers.append(worker)
865
Todd Fiala68615ce2015-09-15 21:38:04 +0000866 # Main loop: wait for all workers to finish and wait for
867 # the socket handlers to wrap up.
868 ctrl_c_loop(
869 # Main operation of loop
870 lambda: pump_workers_and_asyncore_map(
871 workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000872
Todd Fiala68615ce2015-09-15 21:38:04 +0000873 # Return True when we're done with the main loop.
874 lambda: workers_and_async_done(workers, RUNNER_PROCESS_ASYNC_MAP),
Todd Fiala8cbeed32015-09-08 22:22:33 +0000875
Todd Fiala68615ce2015-09-15 21:38:04 +0000876 # Indicate what we do when we receive one or more Ctrl-Cs.
877 lambda ctrl_c_count: handle_ctrl_c(
878 ctrl_c_count, job_queue, workers, inferior_pid_events,
879 kill_all_worker_threads))
Todd Fiala8cbeed32015-09-08 22:22:33 +0000880
Todd Fiala68615ce2015-09-15 21:38:04 +0000881 # Reap the test results.
Todd Fiala8cbeed32015-09-08 22:22:33 +0000882 test_results = []
883 while not result_queue.empty():
884 test_results.append(result_queue.get(block=False))
885 return test_results
886
887
888def threading_test_runner_pool(num_threads, test_work_items):
889 # Initialize our global state.
890 initialize_global_vars_threading(num_threads, test_work_items)
891
Todd Fiala68615ce2015-09-15 21:38:04 +0000892 pool = multiprocessing.pool.ThreadPool(num_threads)
893 map_future = pool.map_async(
894 process_dir_worker_threading_pool, test_work_items)
895
896 return map_async_run_loop(
897 map_future, RUNNER_PROCESS_ASYNC_MAP, RESULTS_LISTENER_CHANNEL)
898
899
900def asyncore_run_loop(channel_map):
901 try:
902 asyncore.loop(None, False, channel_map)
903 except:
904 # Swallow it, we're seeing:
905 # error: (9, 'Bad file descriptor')
906 # when the listener channel is closed. Shouldn't be the case.
907 pass
Todd Fiala8cbeed32015-09-08 22:22:33 +0000908
909
910def inprocess_exec_test_runner(test_work_items):
911 # Initialize our global state.
912 initialize_global_vars_multiprocessing(1, test_work_items)
Todd Fiala8cbeed32015-09-08 22:22:33 +0000913
Todd Fiala33896a92015-09-18 21:01:13 +0000914 # We're always worker index 0
915 global GET_WORKER_INDEX
916 GET_WORKER_INDEX = lambda: 0
917
Todd Fiala68615ce2015-09-15 21:38:04 +0000918 # Run the listener and related channel maps in a separate thread.
919 # global RUNNER_PROCESS_ASYNC_MAP
920 global RESULTS_LISTENER_CHANNEL
921 if RESULTS_LISTENER_CHANNEL is not None:
922 socket_thread = threading.Thread(
923 target=lambda: asyncore_run_loop(RUNNER_PROCESS_ASYNC_MAP))
924 socket_thread.start()
925
926 # Do the work.
Zachary Turner1c4059a2015-10-22 20:39:59 +0000927 test_results = list(map(process_dir_mapper_inprocess, test_work_items))
Todd Fiala68615ce2015-09-15 21:38:04 +0000928
929 # If we have a listener channel, shut it down here.
930 if RESULTS_LISTENER_CHANNEL is not None:
931 # Close down the channel.
932 RESULTS_LISTENER_CHANNEL.close()
933 RESULTS_LISTENER_CHANNEL = None
934
935 # Wait for the listener and handlers to complete.
936 socket_thread.join()
937
938 return test_results
Todd Fiala8cbeed32015-09-08 22:22:33 +0000939
940def walk_and_invoke(test_directory, test_subdir, dotest_argv,
Todd Fiala871b2e52015-09-22 22:47:34 +0000941 num_workers, test_runner_func):
Steve Puccibefe2b12014-03-07 00:01:11 +0000942 """Look for matched files and invoke test driver on each one.
943 In single-threaded mode, each test driver is invoked directly.
944 In multi-threaded mode, submit each test driver to a worker
Vince Harrone06a7a82015-05-12 23:12:19 +0000945 queue, and then wait for all to complete.
946
947 test_directory - lldb/test/ directory
Chaoren Linb6325d02015-08-12 18:02:54 +0000948 test_subdir - lldb/test/ or a subfolder with the tests we're interested in
949 running
Vince Harrone06a7a82015-05-12 23:12:19 +0000950 """
Todd Fiala68615ce2015-09-15 21:38:04 +0000951 # The async_map is important to keep all thread-related asyncore
952 # channels distinct when we call asyncore.loop() later on.
953 global RESULTS_LISTENER_CHANNEL, RUNNER_PROCESS_ASYNC_MAP
954 RUNNER_PROCESS_ASYNC_MAP = {}
955
956 # If we're outputting side-channel test results, create the socket
957 # listener channel and tell the inferior to send results to the
958 # port on which we'll be listening.
959 if RESULTS_FORMATTER is not None:
Todd Fialae83f1402015-09-18 22:45:31 +0000960 forwarding_func = RESULTS_FORMATTER.handle_event
Todd Fiala68615ce2015-09-15 21:38:04 +0000961 RESULTS_LISTENER_CHANNEL = (
962 dotest_channels.UnpicklingForwardingListenerChannel(
Todd Fiala871b2e52015-09-22 22:47:34 +0000963 RUNNER_PROCESS_ASYNC_MAP, "localhost", 0,
964 2 * num_workers, forwarding_func))
Todd Fiala68615ce2015-09-15 21:38:04 +0000965 dotest_argv.append("--results-port")
966 dotest_argv.append(str(RESULTS_LISTENER_CHANNEL.address[1]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000967
968 # Collect the test files that we'll run.
969 test_work_items = []
Todd Fiala8cbeed32015-09-08 22:22:33 +0000970 find_test_files_in_dir_tree(
971 test_subdir, lambda testdir, test_files: test_work_items.append([
Zachary Turner7d564542015-11-02 19:19:49 +0000972 test_subdir, test_files, dotest_argv, None]))
Todd Fiala3f0a3602014-07-08 06:42:37 +0000973
Todd Fiala8cbeed32015-09-08 22:22:33 +0000974 # Convert test work items into test results using whatever
975 # was provided as the test run function.
976 test_results = test_runner_func(test_work_items)
Chaoren Linffc63b02015-08-12 18:02:49 +0000977
Todd Fiala8cbeed32015-09-08 22:22:33 +0000978 # Summarize the results and return to caller.
Chaoren Line80372a2015-08-12 18:02:53 +0000979 timed_out = sum([result[0] for result in test_results], [])
980 passed = sum([result[1] for result in test_results], [])
981 failed = sum([result[2] for result in test_results], [])
Zachary Turner4cceca72015-08-14 16:45:32 +0000982 unexpected_successes = sum([result[3] for result in test_results], [])
983 pass_count = sum([result[4] for result in test_results])
984 fail_count = sum([result[5] for result in test_results])
Todd Fiala3f0a3602014-07-08 06:42:37 +0000985
Todd Fiala8cbeed32015-09-08 22:22:33 +0000986 return (timed_out, passed, failed, unexpected_successes, pass_count,
987 fail_count)
Johnny Chene8d9dc62011-10-31 19:04:07 +0000988
Chaoren Linb6325d02015-08-12 18:02:54 +0000989
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000990def getExpectedTimeouts(platform_name):
Vince Harron06381732015-05-12 23:10:36 +0000991 # returns a set of test filenames that might timeout
992 # are we running against a remote target?
Chaoren Linfebef1b2015-08-19 17:22:12 +0000993 host = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000994 if platform_name is None:
Vince Harron06381732015-05-12 23:10:36 +0000995 target = sys.platform
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000996 else:
Todd Fiala68615ce2015-09-15 21:38:04 +0000997 m = re.search(r'remote-(\w+)', platform_name)
Vince Harronf8b9a1d2015-05-18 19:40:54 +0000998 target = m.group(1)
Vince Harron06381732015-05-12 23:10:36 +0000999
1000 expected_timeout = set()
1001
1002 if target.startswith("linux"):
1003 expected_timeout |= {
Vince Harron06381732015-05-12 23:10:36 +00001004 "TestConnectRemote.py",
1005 "TestCreateAfterAttach.py",
Tamas Berghammer0d0ec9f2015-05-19 10:49:40 +00001006 "TestEvents.py",
Vince Harron06381732015-05-12 23:10:36 +00001007 "TestExitDuringStep.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001008
1009 # Times out in ~10% of the times on the build bot
1010 "TestHelloWorld.py",
Oleksiy Vyalov18f4c9f2015-06-10 01:34:25 +00001011 "TestMultithreaded.py",
Chaoren Linb6325d02015-08-12 18:02:54 +00001012 "TestRegisters.py", # ~12/600 dosep runs (build 3120-3122)
Vince Harron06381732015-05-12 23:10:36 +00001013 "TestThreadStepOut.py",
1014 }
1015 elif target.startswith("android"):
1016 expected_timeout |= {
1017 "TestExitDuringStep.py",
1018 "TestHelloWorld.py",
1019 }
Chaoren Linfebef1b2015-08-19 17:22:12 +00001020 if host.startswith("win32"):
1021 expected_timeout |= {
1022 "TestEvents.py",
1023 "TestThreadStates.py",
1024 }
Ed Maste4dd8fba2015-05-14 16:25:52 +00001025 elif target.startswith("freebsd"):
1026 expected_timeout |= {
1027 "TestBreakpointConditions.py",
Ed Maste08948132015-05-28 18:45:30 +00001028 "TestChangeProcessGroup.py",
Ed Mastebfd05632015-05-27 19:11:29 +00001029 "TestValueObjectRecursion.py",
Ed Maste4dd8fba2015-05-14 16:25:52 +00001030 "TestWatchpointConditionAPI.py",
1031 }
Vince Harron0f173ac2015-05-18 19:36:33 +00001032 elif target.startswith("darwin"):
1033 expected_timeout |= {
Chaoren Linb6325d02015-08-12 18:02:54 +00001034 # times out on MBP Retina, Mid 2012
1035 "TestThreadSpecificBreakpoint.py",
Chaoren Lind9043712015-08-19 17:13:02 +00001036 "TestExitDuringStep.py",
Chaoren Lin99f25be2015-08-20 01:26:57 +00001037 "TestIntegerTypesExpr.py",
Vince Harron0f173ac2015-05-18 19:36:33 +00001038 }
Vince Harron06381732015-05-12 23:10:36 +00001039 return expected_timeout
1040
Chaoren Linb6325d02015-08-12 18:02:54 +00001041
Pavel Labathfad30cf2015-06-29 14:16:51 +00001042def getDefaultTimeout(platform_name):
1043 if os.getenv("LLDB_TEST_TIMEOUT"):
1044 return os.getenv("LLDB_TEST_TIMEOUT")
1045
1046 if platform_name is None:
1047 platform_name = sys.platform
1048
1049 if platform_name.startswith("remote-"):
1050 return "10m"
1051 else:
1052 return "4m"
1053
Chaoren Linb6325d02015-08-12 18:02:54 +00001054
Vince Harron0b9dbb52015-05-21 18:18:52 +00001055def touch(fname, times=None):
Greg Clayton8c3f9c92015-08-11 21:01:32 +00001056 if os.path.exists(fname):
Vince Harron0b9dbb52015-05-21 18:18:52 +00001057 os.utime(fname, times)
1058
Chaoren Linb6325d02015-08-12 18:02:54 +00001059
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001060def find(pattern, path):
1061 result = []
1062 for root, dirs, files in os.walk(path):
1063 for name in files:
1064 if fnmatch.fnmatch(name, pattern):
1065 result.append(os.path.join(root, name))
1066 return result
1067
Chaoren Linb6325d02015-08-12 18:02:54 +00001068
Todd Fiala8cbeed32015-09-08 22:22:33 +00001069def get_test_runner_strategies(num_threads):
1070 """Returns the test runner strategies by name in a dictionary.
1071
1072 @param num_threads specifies the number of threads/processes
1073 that will be used for concurrent test runners.
1074
1075 @return dictionary with key as test runner strategy name and
1076 value set to a callable object that takes the test work item
1077 and returns a test result tuple.
1078 """
1079 return {
1080 # multiprocessing supports ctrl-c and does not use
1081 # multiprocessing.Pool.
1082 "multiprocessing":
1083 (lambda work_items: multiprocessing_test_runner(
1084 num_threads, work_items)),
1085
1086 # multiprocessing-pool uses multiprocessing.Pool but
1087 # does not support Ctrl-C.
1088 "multiprocessing-pool":
1089 (lambda work_items: multiprocessing_test_runner_pool(
1090 num_threads, work_items)),
1091
1092 # threading uses a hand-rolled worker pool much
1093 # like multiprocessing, but instead uses in-process
1094 # worker threads. This one supports Ctrl-C.
1095 "threading":
1096 (lambda work_items: threading_test_runner(num_threads, work_items)),
1097
1098 # threading-pool uses threading for the workers (in-process)
1099 # and uses the multiprocessing.pool thread-enabled pool.
Todd Fiala68615ce2015-09-15 21:38:04 +00001100 # This does not properly support Ctrl-C.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001101 "threading-pool":
1102 (lambda work_items: threading_test_runner_pool(
1103 num_threads, work_items)),
1104
1105 # serial uses the subprocess-based, single process
1106 # test runner. This provides process isolation but
Todd Fiala68615ce2015-09-15 21:38:04 +00001107 # no concurrent test execution.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001108 "serial":
1109 inprocess_exec_test_runner
1110 }
1111
1112
Todd Fialaea736242015-09-23 15:21:28 +00001113def _remove_option(
1114 args, long_option_name, short_option_name, takes_arg):
Todd Fiala68615ce2015-09-15 21:38:04 +00001115 """Removes option and related option arguments from args array.
Todd Fialaea736242015-09-23 15:21:28 +00001116
1117 This method removes all short/long options that match the given
1118 arguments.
1119
Todd Fiala68615ce2015-09-15 21:38:04 +00001120 @param args the array of command line arguments (in/out)
Todd Fialaea736242015-09-23 15:21:28 +00001121
1122 @param long_option_name the full command line representation of the
1123 long-form option that will be removed (including '--').
1124
1125 @param short_option_name the short version of the command line option
1126 that will be removed (including '-').
1127
1128 @param takes_arg True if the option takes an argument.
1129
Todd Fiala68615ce2015-09-15 21:38:04 +00001130 """
Todd Fialaea736242015-09-23 15:21:28 +00001131 if long_option_name is not None:
1132 regex_string = "^" + long_option_name + "="
1133 long_regex = re.compile(regex_string)
1134 if short_option_name is not None:
1135 # Short options we only match the -X and assume
1136 # any arg is one command line argument jammed together.
1137 # i.e. -O--abc=1 is a single argument in the args list.
1138 # We don't handle -O --abc=1, as argparse doesn't handle
1139 # it, either.
1140 regex_string = "^" + short_option_name
1141 short_regex = re.compile(regex_string)
1142
1143 def remove_long_internal():
1144 """Removes one matching long option from args.
1145 @returns True if one was found and removed; False otherwise.
1146 """
1147 try:
1148 index = args.index(long_option_name)
1149 # Handle the exact match case.
1150 if takes_arg:
1151 removal_count = 2
1152 else:
1153 removal_count = 1
1154 del args[index:index+removal_count]
1155 return True
1156 except ValueError:
1157 # Thanks to argparse not handling options with known arguments
1158 # like other options parsing libraries (see
1159 # https://bugs.python.org/issue9334), we need to support the
1160 # --results-formatter-options={second-level-arguments} (note
1161 # the equal sign to fool the first-level arguments parser into
1162 # not treating the second-level arguments as first-level
1163 # options). We're certainly at risk of getting this wrong
1164 # since now we're forced into the business of trying to figure
1165 # out what is an argument (although I think this
1166 # implementation will suffice).
1167 for index in range(len(args)):
1168 match = long_regex.search(args[index])
1169 if match:
1170 del args[index]
1171 return True
1172 return False
1173
1174 def remove_short_internal():
1175 """Removes one matching short option from args.
1176 @returns True if one was found and removed; False otherwise.
1177 """
Todd Fiala68615ce2015-09-15 21:38:04 +00001178 for index in range(len(args)):
Todd Fialaea736242015-09-23 15:21:28 +00001179 match = short_regex.search(args[index])
Todd Fiala68615ce2015-09-15 21:38:04 +00001180 if match:
Todd Fiala68615ce2015-09-15 21:38:04 +00001181 del args[index]
Todd Fialaea736242015-09-23 15:21:28 +00001182 return True
1183 return False
Todd Fiala68615ce2015-09-15 21:38:04 +00001184
Todd Fialaea736242015-09-23 15:21:28 +00001185 removal_count = 0
1186 while long_option_name is not None and remove_long_internal():
1187 removal_count += 1
1188 while short_option_name is not None and remove_short_internal():
1189 removal_count += 1
1190 if removal_count == 0:
1191 raise Exception(
1192 "failed to find at least one of '{}', '{}' in options".format(
1193 long_option_name, short_option_name))
Todd Fiala68615ce2015-09-15 21:38:04 +00001194
1195
1196def adjust_inferior_options(dotest_argv):
1197 """Adjusts the commandline args array for inferiors.
1198
1199 This method adjusts the inferior dotest commandline options based
1200 on the parallel test runner's options. Some of the inferior options
1201 will need to change to properly handle aggregation functionality.
1202 """
1203 global dotest_options
1204
1205 # If we don't have a session directory, create one.
1206 if not dotest_options.s:
1207 # no session log directory, we need to add this to prevent
1208 # every dotest invocation from creating its own directory
1209 import datetime
1210 # The windows platforms don't like ':' in the pathname.
Zachary Turneraf383ff2015-10-27 22:33:47 +00001211 timestamp_started = datetime.datetime.now().strftime("%Y-%m-%d-%H_%M_%S")
Todd Fiala68615ce2015-09-15 21:38:04 +00001212 dotest_argv.append('-s')
1213 dotest_argv.append(timestamp_started)
1214 dotest_options.s = timestamp_started
1215
1216 # Adjust inferior results formatter options - if the parallel
1217 # test runner is collecting into the user-specified test results,
1218 # we'll have inferiors spawn with the --results-port option and
1219 # strip the original test runner options.
1220 if dotest_options.results_file is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001221 _remove_option(dotest_argv, "--results-file", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001222 if dotest_options.results_port is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001223 _remove_option(dotest_argv, "--results-port", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001224 if dotest_options.results_formatter is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001225 _remove_option(dotest_argv, "--results-formatter", None, True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001226 if dotest_options.results_formatter_options is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001227 _remove_option(dotest_argv, "--results-formatter-option", "-O",
1228 True)
Todd Fiala68615ce2015-09-15 21:38:04 +00001229
Todd Fialacee6a6a2015-11-09 18:51:04 +00001230 # Remove the --curses shortcut if specified.
1231 if dotest_options.curses:
1232 _remove_option(dotest_argv, "--curses", None, False)
1233
Todd Fiala33896a92015-09-18 21:01:13 +00001234 # Remove test runner name if present.
1235 if dotest_options.test_runner_name is not None:
Todd Fialaea736242015-09-23 15:21:28 +00001236 _remove_option(dotest_argv, "--test-runner-name", None, True)
Todd Fiala33896a92015-09-18 21:01:13 +00001237
1238
Todd Fiala83c32e32015-09-22 21:19:40 +00001239def is_darwin_version_lower_than(target_version):
1240 """Checks that os is Darwin and version is lower than target_version.
1241
1242 @param target_version the StrictVersion indicating the version
1243 we're checking against.
1244
1245 @return True if the OS is Darwin (OS X) and the version number of
1246 the OS is less than target_version; False in all other cases.
1247 """
1248 if platform.system() != 'Darwin':
1249 # Can't be Darwin lower than a certain version.
1250 return False
1251
1252 system_version = distutils.version.StrictVersion(platform.mac_ver()[0])
Zachary Turnerbac6e4f2015-11-03 21:37:27 +00001253 return seven.cmp_(system_version, target_version) < 0
Todd Fiala83c32e32015-09-22 21:19:40 +00001254
1255
1256def default_test_runner_name(num_threads):
1257 """Returns the default test runner name for the configuration.
1258
1259 @param num_threads the number of threads/workers this test runner is
1260 supposed to use.
1261
1262 @return the test runner name that should be used by default when
1263 no test runner was explicitly called out on the command line.
1264 """
1265 if num_threads == 1:
1266 # Use the serial runner.
1267 test_runner_name = "serial"
1268 elif os.name == "nt":
Adrian McCarthy040b31d2015-10-12 14:46:57 +00001269 # On Windows, Python uses CRT with a low limit on the number of open
1270 # files. If you have a lot of cores, the threading-pool runner will
Zachary Turnerf0c3f682015-11-06 18:14:31 +00001271 # often fail because it exceeds that limit. It's not clear what the
1272 # right balance is, so until we can investigate it more deeply,
1273 # just use the one that works
1274 test_runner_name = "multiprocessing-pool"
Todd Fiala83c32e32015-09-22 21:19:40 +00001275 elif is_darwin_version_lower_than(
1276 distutils.version.StrictVersion("10.10.0")):
1277 # OS X versions before 10.10 appear to have an issue using
1278 # the threading test runner. Fall back to multiprocessing.
1279 # Supports Ctrl-C.
1280 test_runner_name = "multiprocessing"
1281 else:
1282 # For everyone else, use the ctrl-c-enabled threading support.
1283 # Should use fewer system resources than the multprocessing
1284 # variant.
1285 test_runner_name = "threading"
1286 return test_runner_name
1287
1288
Todd Fiala8cbeed32015-09-08 22:22:33 +00001289def main(print_details_on_success, num_threads, test_subdir,
Todd Fiala68615ce2015-09-15 21:38:04 +00001290 test_runner_name, results_formatter):
Todd Fialafed95662015-09-03 18:58:44 +00001291 """Run dotest.py in inferior mode in parallel.
1292
1293 @param print_details_on_success the parsed value of the output-on-success
1294 command line argument. When True, details of a successful dotest inferior
1295 are printed even when everything succeeds. The normal behavior is to
1296 not print any details when all the inferior tests pass.
1297
1298 @param num_threads the parsed value of the num-threads command line
1299 argument.
1300
1301 @param test_subdir optionally specifies a subdir to limit testing
1302 within. May be None if the entire test tree is to be used. This subdir
1303 is assumed to be relative to the lldb/test root of the test hierarchy.
Todd Fiala8cbeed32015-09-08 22:22:33 +00001304
1305 @param test_runner_name if specified, contains the test runner
1306 name which selects the strategy used to run the isolated and
1307 optionally concurrent test runner. Specify None to allow the
1308 system to choose the most appropriate test runner given desired
1309 thread count and OS type.
1310
Todd Fiala68615ce2015-09-15 21:38:04 +00001311 @param results_formatter if specified, provides the TestResultsFormatter
1312 instance that will format and output test result data from the
1313 side-channel test results. When specified, inferior dotest calls
1314 will send test results side-channel data over a socket to the parallel
1315 test runner, which will forward them on to results_formatter.
Todd Fialafed95662015-09-03 18:58:44 +00001316 """
1317
Todd Fiala1cc97b42015-09-21 05:42:26 +00001318 # Do not shut down on sighup.
Ying Chend93aa102015-09-23 21:53:18 +00001319 if hasattr(signal, 'SIGHUP'):
1320 signal.signal(signal.SIGHUP, signal.SIG_IGN)
Todd Fiala1cc97b42015-09-21 05:42:26 +00001321
Todd Fialafed95662015-09-03 18:58:44 +00001322 dotest_argv = sys.argv[1:]
1323
Greg Claytonb0d148e2015-09-21 17:25:01 +00001324 global output_on_success, RESULTS_FORMATTER
Todd Fialafed95662015-09-03 18:58:44 +00001325 output_on_success = print_details_on_success
Todd Fiala68615ce2015-09-15 21:38:04 +00001326 RESULTS_FORMATTER = results_formatter
Todd Fialafed95662015-09-03 18:58:44 +00001327
Vince Harrond5fa1022015-05-10 15:24:12 +00001328 # We can't use sys.path[0] to determine the script directory
1329 # because it doesn't work under a debugger
Vince Harron8994fed2015-05-22 19:49:23 +00001330 parser = dotest_args.create_parser()
Pavel Labath05ab2372015-07-06 15:57:52 +00001331 global dotest_options
Vince Harron8994fed2015-05-22 19:49:23 +00001332 dotest_options = dotest_args.parse_args(parser, dotest_argv)
1333
Todd Fiala68615ce2015-09-15 21:38:04 +00001334 adjust_inferior_options(dotest_argv)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001335
1336 session_dir = os.path.join(os.getcwd(), dotest_options.s)
Ed Mastecec2a5b2014-11-21 02:41:25 +00001337
Vince Harrone06a7a82015-05-12 23:12:19 +00001338 # The root directory was specified on the command line
Todd Fiala68615ce2015-09-15 21:38:04 +00001339 test_directory = os.path.dirname(os.path.realpath(__file__))
Todd Fialafed95662015-09-03 18:58:44 +00001340 if test_subdir and len(test_subdir) > 0:
1341 test_subdir = os.path.join(test_directory, test_subdir)
Vince Harrone06a7a82015-05-12 23:12:19 +00001342 else:
Todd Fialafed95662015-09-03 18:58:44 +00001343 test_subdir = test_directory
Vince Harrone06a7a82015-05-12 23:12:19 +00001344
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001345 # clean core files in test tree from previous runs (Linux)
1346 cores = find('core.*', test_subdir)
1347 for core in cores:
1348 os.unlink(core)
1349
Daniel Maleab42556f2013-04-19 18:32:53 +00001350 system_info = " ".join(platform.uname())
Todd Fiala8cbeed32015-09-08 22:22:33 +00001351
1352 # Figure out which testrunner strategy we'll use.
1353 runner_strategies_by_name = get_test_runner_strategies(num_threads)
1354
1355 # If the user didn't specify a test runner strategy, determine
1356 # the default now based on number of threads and OS type.
1357 if not test_runner_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001358 test_runner_name = default_test_runner_name(num_threads)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001359
1360 if test_runner_name not in runner_strategies_by_name:
Todd Fiala83c32e32015-09-22 21:19:40 +00001361 raise Exception(
1362 "specified testrunner name '{}' unknown. Valid choices: {}".format(
1363 test_runner_name,
Zachary Turner606e1e32015-10-23 17:53:51 +00001364 list(runner_strategies_by_name.keys())))
Todd Fiala8cbeed32015-09-08 22:22:33 +00001365 test_runner_func = runner_strategies_by_name[test_runner_name]
1366
1367 summary_results = walk_and_invoke(
Todd Fiala871b2e52015-09-22 22:47:34 +00001368 test_directory, test_subdir, dotest_argv,
1369 num_threads, test_runner_func)
Todd Fiala8cbeed32015-09-08 22:22:33 +00001370
1371 (timed_out, passed, failed, unexpected_successes, pass_count,
1372 fail_count) = summary_results
Zachary Turnerc7a7c8a2015-05-28 19:56:26 +00001373
Todd Fialade9a44e2015-09-22 00:15:50 +00001374 # The results formatter - if present - is done now. Tell it to
1375 # terminate.
1376 if results_formatter is not None:
1377 results_formatter.send_terminate_as_needed()
1378
Vince Harron17f429f2014-12-13 00:08:19 +00001379 timed_out = set(timed_out)
Chaoren Line80372a2015-08-12 18:02:53 +00001380 num_test_files = len(passed) + len(failed)
1381 num_test_cases = pass_count + fail_count
Daniel Maleab42556f2013-04-19 18:32:53 +00001382
Vince Harrondcc2b9f2015-05-27 04:40:36 +00001383 # move core files into session dir
1384 cores = find('core.*', test_subdir)
1385 for core in cores:
1386 dst = core.replace(test_directory, "")[1:]
1387 dst = dst.replace(os.path.sep, "-")
1388 os.rename(core, os.path.join(session_dir, dst))
1389
Vince Harron06381732015-05-12 23:10:36 +00001390 # remove expected timeouts from failures
Vince Harronf8b9a1d2015-05-18 19:40:54 +00001391 expected_timeout = getExpectedTimeouts(dotest_options.lldb_platform_name)
Vince Harron06381732015-05-12 23:10:36 +00001392 for xtime in expected_timeout:
1393 if xtime in timed_out:
1394 timed_out.remove(xtime)
1395 failed.remove(xtime)
Vince Harron0b9dbb52015-05-21 18:18:52 +00001396 result = "ExpectedTimeout"
1397 elif xtime in passed:
1398 result = "UnexpectedCompletion"
1399 else:
1400 result = None # failed
1401
1402 if result:
1403 test_name = os.path.splitext(xtime)[0]
1404 touch(os.path.join(session_dir, "{}-{}".format(result, test_name)))
Vince Harron06381732015-05-12 23:10:36 +00001405
Zachary Turnerff890da2015-10-19 23:45:41 +00001406 print()
Chaoren Lin5a59e462015-08-12 18:02:51 +00001407 sys.stdout.write("Ran %d test suites" % num_test_files)
1408 if num_test_files > 0:
1409 sys.stdout.write(" (%d failed) (%f%%)" % (
1410 len(failed), 100.0 * len(failed) / num_test_files))
Zachary Turnerff890da2015-10-19 23:45:41 +00001411 print()
Chaoren Line80372a2015-08-12 18:02:53 +00001412 sys.stdout.write("Ran %d test cases" % num_test_cases)
1413 if num_test_cases > 0:
Chaoren Lin5a59e462015-08-12 18:02:51 +00001414 sys.stdout.write(" (%d failed) (%f%%)" % (
Chaoren Line80372a2015-08-12 18:02:53 +00001415 fail_count, 100.0 * fail_count / num_test_cases))
Zachary Turnerff890da2015-10-19 23:45:41 +00001416 print()
Zachary Turner4cceca72015-08-14 16:45:32 +00001417 exit_code = 0
1418
Daniel Maleacbaef262013-02-15 21:31:37 +00001419 if len(failed) > 0:
Shawn Best13491c42014-10-22 19:29:00 +00001420 failed.sort()
Zachary Turnerff890da2015-10-19 23:45:41 +00001421 print("Failing Tests (%d)" % len(failed))
Daniel Maleacbaef262013-02-15 21:31:37 +00001422 for f in failed:
Zachary Turnerff890da2015-10-19 23:45:41 +00001423 print("%s: LLDB (suite) :: %s (%s)" % (
Vince Harron17f429f2014-12-13 00:08:19 +00001424 "TIMEOUT" if f in timed_out else "FAIL", f, system_info
Zachary Turnerff890da2015-10-19 23:45:41 +00001425 ))
Zachary Turner4cceca72015-08-14 16:45:32 +00001426 exit_code = 1
1427
1428 if len(unexpected_successes) > 0:
1429 unexpected_successes.sort()
Zachary Turnerff890da2015-10-19 23:45:41 +00001430 print("\nUnexpected Successes (%d)" % len(unexpected_successes))
Zachary Turner4cceca72015-08-14 16:45:32 +00001431 for u in unexpected_successes:
Zachary Turnerff890da2015-10-19 23:45:41 +00001432 print("UNEXPECTED SUCCESS: LLDB (suite) :: %s (%s)" % (u, system_info))
Zachary Turner4cceca72015-08-14 16:45:32 +00001433
1434 sys.exit(exit_code)
Johnny Chene8d9dc62011-10-31 19:04:07 +00001435
1436if __name__ == '__main__':
Todd Fialafed95662015-09-03 18:58:44 +00001437 sys.stderr.write(
1438 "error: dosep.py no longer supports being called directly. "
1439 "Please call dotest.py directly. The dosep.py-specific arguments "
1440 "have been added under the Parallel processing arguments.\n")
1441 sys.exit(128)