# HG changeset patch # User Matt Mackall # Date 1303681335 18000 # Node ID 9c4da6ab4e5a6982ebe8d5eb6581b83965307d68 # Parent 636a6f5aa2cdc31037f655e7ec19e387635c8638 run-tests: switch timeout handling from alarm to helper thread This should be slightly more portable than signals and be compatible with threaded dispatch. diff -r 636a6f5aa2cd -r 9c4da6ab4e5a tests/run-tests.py --- a/tests/run-tests.py Sun Apr 24 16:42:11 2011 -0500 +++ b/tests/run-tests.py Sun Apr 24 16:42:15 2011 -0500 @@ -56,14 +56,29 @@ import threading closefds = os.name == 'posix' -def Popen4(cmd, bufsize=-1): - p = subprocess.Popen(cmd, shell=True, bufsize=bufsize, +def Popen4(cmd, timeout): + p = subprocess.Popen(cmd, shell=True, bufsize=-1, close_fds=closefds, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p.fromchild = p.stdout p.tochild = p.stdin p.childerr = p.stderr + + if timeout: + p.timeout = False + def t(): + start = time.time() + while time.time() - start < timeout and p.returncode is None: + time.sleep(1) + p.timeout = True + if p.returncode is None: + try: + p.terminate() + except OSError: + pass + threading.Thread(target=t).start() + return p # reserved exit code to skip test (used by hghave) @@ -440,12 +455,6 @@ os.mkdir(adir) covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit) -class Timeout(Exception): - pass - -def alarmed(signum, frame): - raise Timeout - def pytest(test, options, replacements): py3kswitch = options.py3k_warnings and ' -3' or '' cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test) @@ -603,33 +612,38 @@ if ret is None: ret = 0 else: - proc = Popen4(cmd) + proc = Popen4(cmd, options.timeout) def cleanup(): - os.kill(proc.pid, signal.SIGTERM) + try: + proc.terminate() + except OSError: + pass ret = proc.wait() if ret == 0: ret = signal.SIGTERM << 8 killdaemons() return ret + output = '' + proc.tochild.close() + try: - output = '' - proc.tochild.close() output = proc.fromchild.read() - ret = proc.wait() - if wifexited(ret): - ret = os.WEXITSTATUS(ret) - except Timeout: - vlog('# Process %d timed out - killing it' % proc.pid) - cleanup() - ret = 'timeout' - output += ("\n### Abort: timeout after %d seconds.\n" - % options.timeout) except KeyboardInterrupt: vlog('# Handling keyboard interrupt') cleanup() raise + ret = proc.wait() + if wifexited(ret): + ret = os.WEXITSTATUS(ret) + + if proc.timeout: + ret = 'timeout' + + if ret: + killdaemons() + for s, r in replacements: output = re.sub(s, r, output) return ret, splitnewlines(output) @@ -755,9 +769,6 @@ os.mkdir(testtmp) os.chdir(testtmp) - if options.timeout > 0: - signal.alarm(options.timeout) - ret, out = runner(testpath, options, [ (re.escape(testtmp), '$TESTTMP'), (r':%s\b' % options.port, ':$HGPORT'), @@ -766,9 +777,6 @@ ]) vlog("# Ret was:", ret) - if options.timeout > 0: - signal.alarm(0) - mark = '.' if ret == 0: success() @@ -807,11 +815,12 @@ skipped = False else: skip(missing[-1]) + elif ret == 'timeout': + mark = 't' + fail("timed out", ret) elif out != refout: mark = '!' - if ret == 'timeout': - fail("timed out", ret) - elif ret: + if ret: fail("output changed and returned error code %d" % ret, ret) else: fail("output changed", ret) @@ -962,15 +971,6 @@ installhg(options) _checkhglib("Testing") - if options.timeout > 0: - try: - signal.signal(signal.SIGALRM, alarmed) - vlog('# Running each test with %d second timeout' % - options.timeout) - except AttributeError: - print 'WARNING: cannot run tests with timeouts' - options.timeout = 0 - if options.restart: orig = list(tests) while tests: