| 
									
										
										
										
											2016-03-23 02:04:32 +01:00
										 |  |  | import faulthandler | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  | import json | 
					
						
							|  |  |  | import os | 
					
						
							| 
									
										
										
										
											2015-09-30 03:05:43 +02:00
										 |  |  | import queue | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  | import time | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  | import traceback | 
					
						
							| 
									
										
										
										
											2015-09-30 01:32:39 +02:00
										 |  |  | import types | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  | from test import support | 
					
						
							|  |  |  | try: | 
					
						
							|  |  |  |     import threading | 
					
						
							|  |  |  | except ImportError: | 
					
						
							|  |  |  |     print("Multiprocess option requires thread support") | 
					
						
							|  |  |  |     sys.exit(2) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 12:14:10 +01:00
										 |  |  | from test.libregrtest.runtest import ( | 
					
						
							| 
									
										
										
										
											2016-05-20 13:37:40 +02:00
										 |  |  |     runtest, INTERRUPTED, CHILD_ERROR, PROGRESS_MIN_TIME, | 
					
						
							|  |  |  |     format_test_result) | 
					
						
							| 
									
										
										
										
											2015-09-30 02:17:28 +02:00
										 |  |  | from test.libregrtest.setup import setup_tests | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-30 03:05:43 +02:00
										 |  |  | # Display the running tests if nothing happened last N seconds | 
					
						
							| 
									
										
										
										
											2015-11-04 09:03:53 +01:00
										 |  |  | PROGRESS_UPDATE = 30.0   # seconds | 
					
						
							| 
									
										
										
										
											2015-09-30 03:05:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 09:50:18 +02:00
										 |  |  | # If interrupted, display the wait progress every N seconds | 
					
						
							| 
									
										
										
										
											2016-03-24 12:04:15 +01:00
										 |  |  | WAIT_PROGRESS = 2.0   # seconds | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def run_test_in_subprocess(testname, ns): | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     """Run the given test in a subprocess with --slaveargs.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ns is the option Namespace parsed from command-line arguments. regrtest | 
					
						
							|  |  |  |     is invoked in a subprocess with the --slaveargs argument; when the | 
					
						
							|  |  |  |     subprocess exits, its return code, stdout and stderr are returned as a | 
					
						
							|  |  |  |     3-tuple. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     from subprocess import Popen, PIPE | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-30 01:32:39 +02:00
										 |  |  |     ns_dict = vars(ns) | 
					
						
							|  |  |  |     slaveargs = (ns_dict, testname) | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  |     slaveargs = json.dumps(slaveargs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cmd = [sys.executable, *support.args_from_interpreter_flags(), | 
					
						
							| 
									
										
										
										
											2016-09-21 17:12:50 +02:00
										 |  |  |            '-u',    # Unbuffered stdout and stderr | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  |            '-m', 'test.regrtest', | 
					
						
							|  |  |  |            '--slaveargs', slaveargs] | 
					
						
							| 
									
										
										
										
											2015-10-02 16:20:49 -07:00
										 |  |  |     if ns.pgo: | 
					
						
							|  |  |  |         cmd += ['--pgo'] | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     # Running the child from the same working directory as regrtest's original | 
					
						
							|  |  |  |     # invocation ensures that TEMPDIR for the child is the same when | 
					
						
							|  |  |  |     # sysconfig.is_python_build() is true. See issue 15300. | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  |     popen = Popen(cmd, | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |                   stdout=PIPE, stderr=PIPE, | 
					
						
							|  |  |  |                   universal_newlines=True, | 
					
						
							|  |  |  |                   close_fds=(os.name != 'nt'), | 
					
						
							|  |  |  |                   cwd=support.SAVEDCWD) | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  |     with popen: | 
					
						
							|  |  |  |         stdout, stderr = popen.communicate() | 
					
						
							|  |  |  |         retcode = popen.wait() | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     return retcode, stdout, stderr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def run_tests_slave(slaveargs): | 
					
						
							| 
									
										
										
										
											2015-09-30 01:32:39 +02:00
										 |  |  |     ns_dict, testname = json.loads(slaveargs) | 
					
						
							|  |  |  |     ns = types.SimpleNamespace(**ns_dict) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-30 02:17:28 +02:00
										 |  |  |     setup_tests(ns) | 
					
						
							| 
									
										
										
										
											2015-09-30 01:32:39 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2015-09-30 02:32:11 +02:00
										 |  |  |         result = runtest(ns, testname) | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     except KeyboardInterrupt: | 
					
						
							|  |  |  |         result = INTERRUPTED, '' | 
					
						
							|  |  |  |     except BaseException as e: | 
					
						
							|  |  |  |         traceback.print_exc() | 
					
						
							|  |  |  |         result = CHILD_ERROR, str(e) | 
					
						
							| 
									
										
										
										
											2015-09-30 02:39:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     print()   # Force a newline (just in case) | 
					
						
							| 
									
										
										
										
											2015-09-30 00:48:27 +02:00
										 |  |  |     print(json.dumps(result), flush=True) | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     sys.exit(0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # We do not use a generator so multiple threads can call next(). | 
					
						
							|  |  |  | class MultiprocessIterator: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """A thread-safe iterator over tests for multiprocess mode.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, tests): | 
					
						
							|  |  |  |         self.interrupted = False | 
					
						
							|  |  |  |         self.lock = threading.Lock() | 
					
						
							|  |  |  |         self.tests = tests | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __iter__(self): | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __next__(self): | 
					
						
							|  |  |  |         with self.lock: | 
					
						
							|  |  |  |             if self.interrupted: | 
					
						
							|  |  |  |                 raise StopIteration('tests interrupted') | 
					
						
							|  |  |  |             return next(self.tests) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MultiprocessThread(threading.Thread): | 
					
						
							|  |  |  |     def __init__(self, pending, output, ns): | 
					
						
							|  |  |  |         super().__init__() | 
					
						
							|  |  |  |         self.pending = pending | 
					
						
							|  |  |  |         self.output = output | 
					
						
							|  |  |  |         self.ns = ns | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  |         self.current_test = None | 
					
						
							|  |  |  |         self.start_time = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _runtest(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             test = next(self.pending) | 
					
						
							|  |  |  |         except StopIteration: | 
					
						
							|  |  |  |             self.output.put((None, None, None, None)) | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.start_time = time.monotonic() | 
					
						
							|  |  |  |             self.current_test = test | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             retcode, stdout, stderr = run_test_in_subprocess(test, self.ns) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self.current_test = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         stdout, _, result = stdout.strip().rpartition("\n") | 
					
						
							|  |  |  |         if retcode != 0: | 
					
						
							|  |  |  |             result = (CHILD_ERROR, "Exit code %s" % retcode) | 
					
						
							|  |  |  |             self.output.put((test, stdout.rstrip(), stderr.rstrip(), | 
					
						
							|  |  |  |                              result)) | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not result: | 
					
						
							|  |  |  |             self.output.put((None, None, None, None)) | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result = json.loads(result) | 
					
						
							|  |  |  |         self.output.put((test, stdout.rstrip(), stderr.rstrip(), | 
					
						
							|  |  |  |                          result)) | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def run(self): | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  |             stop = False | 
					
						
							|  |  |  |             while not stop: | 
					
						
							|  |  |  |                 stop = self._runtest() | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |         except BaseException: | 
					
						
							|  |  |  |             self.output.put((None, None, None, None)) | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def run_tests_multiprocess(regrtest): | 
					
						
							| 
									
										
										
										
											2015-09-30 03:05:43 +02:00
										 |  |  |     output = queue.Queue() | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     pending = MultiprocessIterator(regrtest.tests) | 
					
						
							| 
									
										
										
										
											2016-03-23 02:04:32 +01:00
										 |  |  |     test_timeout = regrtest.ns.timeout | 
					
						
							|  |  |  |     use_timeout = (test_timeout is not None) | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     workers = [MultiprocessThread(pending, output, regrtest.ns) | 
					
						
							|  |  |  |                for i in range(regrtest.ns.use_mp)] | 
					
						
							| 
									
										
										
										
											2016-03-24 11:55:29 +01:00
										 |  |  |     print("Run tests in parallel using %s child processes" | 
					
						
							|  |  |  |           % len(workers)) | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     for worker in workers: | 
					
						
							|  |  |  |         worker.start() | 
					
						
							| 
									
										
										
										
											2015-09-30 03:05:43 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_running(workers): | 
					
						
							|  |  |  |         running = [] | 
					
						
							|  |  |  |         for worker in workers: | 
					
						
							|  |  |  |             current_test = worker.current_test | 
					
						
							|  |  |  |             if not current_test: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             dt = time.monotonic() - worker.start_time | 
					
						
							|  |  |  |             if dt >= PROGRESS_MIN_TIME: | 
					
						
							|  |  |  |                 running.append('%s (%.0f sec)' % (current_test, dt)) | 
					
						
							|  |  |  |         return running | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     finished = 0 | 
					
						
							|  |  |  |     test_index = 1 | 
					
						
							| 
									
										
										
										
											2016-03-23 02:04:32 +01:00
										 |  |  |     get_timeout = max(PROGRESS_UPDATE, PROGRESS_MIN_TIME) | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |     try: | 
					
						
							|  |  |  |         while finished < regrtest.ns.use_mp: | 
					
						
							| 
									
										
										
										
											2016-03-23 02:04:32 +01:00
										 |  |  |             if use_timeout: | 
					
						
							|  |  |  |                 faulthandler.dump_traceback_later(test_timeout, exit=True) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-30 03:05:43 +02:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2016-03-23 02:04:32 +01:00
										 |  |  |                 item = output.get(timeout=get_timeout) | 
					
						
							| 
									
										
										
										
											2015-09-30 03:05:43 +02:00
										 |  |  |             except queue.Empty: | 
					
						
							|  |  |  |                 running = get_running(workers) | 
					
						
							| 
									
										
										
										
											2015-10-02 16:20:49 -07:00
										 |  |  |                 if running and not regrtest.ns.pgo: | 
					
						
							| 
									
										
										
										
											2015-09-30 03:05:43 +02:00
										 |  |  |                     print('running: %s' % ', '.join(running)) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             test, stdout, stderr, result = item | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |             if test is None: | 
					
						
							|  |  |  |                 finished += 1 | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             regrtest.accumulate_result(test, result) | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Display progress | 
					
						
							|  |  |  |             ok, test_time = result | 
					
						
							| 
									
										
										
										
											2016-05-20 13:37:40 +02:00
										 |  |  |             text = format_test_result(test, ok) | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  |             if (ok not in (CHILD_ERROR, INTERRUPTED) | 
					
						
							| 
									
										
										
										
											2015-10-02 16:20:49 -07:00
										 |  |  |                 and test_time >= PROGRESS_MIN_TIME | 
					
						
							|  |  |  |                 and not regrtest.ns.pgo): | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  |                 text += ' (%.0f sec)' % test_time | 
					
						
							| 
									
										
										
										
											2015-09-30 03:05:43 +02:00
										 |  |  |             running = get_running(workers) | 
					
						
							| 
									
										
										
										
											2015-10-02 16:20:49 -07:00
										 |  |  |             if running and not regrtest.ns.pgo: | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  |                 text += ' -- running: %s' % ', '.join(running) | 
					
						
							|  |  |  |             regrtest.display_progress(test_index, text) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Copy stdout and stderr from the child process | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |             if stdout: | 
					
						
							| 
									
										
										
										
											2015-09-30 00:48:27 +02:00
										 |  |  |                 print(stdout, flush=True) | 
					
						
							| 
									
										
										
										
											2015-10-02 16:20:49 -07:00
										 |  |  |             if stderr and not regrtest.ns.pgo: | 
					
						
							| 
									
										
										
										
											2015-09-30 00:48:27 +02:00
										 |  |  |                 print(stderr, file=sys.stderr, flush=True) | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |             if result[0] == INTERRUPTED: | 
					
						
							|  |  |  |                 raise KeyboardInterrupt | 
					
						
							|  |  |  |             if result[0] == CHILD_ERROR: | 
					
						
							| 
									
										
										
										
											2015-09-29 23:43:33 +02:00
										 |  |  |                 msg = "Child error on {}: {}".format(test, result[1]) | 
					
						
							|  |  |  |                 raise Exception(msg) | 
					
						
							| 
									
										
										
										
											2015-09-29 23:15:38 +02:00
										 |  |  |             test_index += 1 | 
					
						
							|  |  |  |     except KeyboardInterrupt: | 
					
						
							|  |  |  |         regrtest.interrupted = True | 
					
						
							|  |  |  |         pending.interrupted = True | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  |         print() | 
					
						
							| 
									
										
										
										
											2016-03-23 02:04:32 +01:00
										 |  |  |     finally: | 
					
						
							|  |  |  |         if use_timeout: | 
					
						
							|  |  |  |             faulthandler.cancel_dump_traceback_later() | 
					
						
							| 
									
										
										
										
											2015-09-30 00:33:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-24 12:04:15 +01:00
										 |  |  |     # If tests are interrupted, wait until tests complete | 
					
						
							|  |  |  |     wait_start = time.monotonic() | 
					
						
							|  |  |  |     while True: | 
					
						
							|  |  |  |         running = [worker.current_test for worker in workers] | 
					
						
							|  |  |  |         running = list(filter(bool, running)) | 
					
						
							|  |  |  |         if not running: | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         dt = time.monotonic() - wait_start | 
					
						
							|  |  |  |         line = "Waiting for %s (%s tests)" % (', '.join(running), len(running)) | 
					
						
							|  |  |  |         if dt >= WAIT_PROGRESS: | 
					
						
							|  |  |  |             line = "%s since %.0f sec" % (line, dt) | 
					
						
							|  |  |  |         print(line) | 
					
						
							|  |  |  |         for worker in workers: | 
					
						
							|  |  |  |             worker.join(WAIT_PROGRESS) |