54 import time |
54 import time |
55 import re |
55 import re |
56 import threading |
56 import threading |
57 |
57 |
58 closefds = os.name == 'posix' |
58 closefds = os.name == 'posix' |
59 def Popen4(cmd, bufsize=-1): |
59 def Popen4(cmd, timeout): |
60 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize, |
60 p = subprocess.Popen(cmd, shell=True, bufsize=-1, |
61 close_fds=closefds, |
61 close_fds=closefds, |
62 stdin=subprocess.PIPE, stdout=subprocess.PIPE, |
62 stdin=subprocess.PIPE, stdout=subprocess.PIPE, |
63 stderr=subprocess.STDOUT) |
63 stderr=subprocess.STDOUT) |
64 p.fromchild = p.stdout |
64 p.fromchild = p.stdout |
65 p.tochild = p.stdin |
65 p.tochild = p.stdin |
66 p.childerr = p.stderr |
66 p.childerr = p.stderr |
|
67 |
|
68 if timeout: |
|
69 p.timeout = False |
|
70 def t(): |
|
71 start = time.time() |
|
72 while time.time() - start < timeout and p.returncode is None: |
|
73 time.sleep(1) |
|
74 p.timeout = True |
|
75 if p.returncode is None: |
|
76 try: |
|
77 p.terminate() |
|
78 except OSError: |
|
79 pass |
|
80 threading.Thread(target=t).start() |
|
81 |
67 return p |
82 return p |
68 |
83 |
69 # reserved exit code to skip test (used by hghave) |
84 # reserved exit code to skip test (used by hghave) |
70 SKIPPED_STATUS = 80 |
85 SKIPPED_STATUS = 80 |
71 SKIPPED_PREFIX = 'skipped: ' |
86 SKIPPED_PREFIX = 'skipped: ' |
438 adir = os.path.join(TESTDIR, 'annotated') |
453 adir = os.path.join(TESTDIR, 'annotated') |
439 if not os.path.isdir(adir): |
454 if not os.path.isdir(adir): |
440 os.mkdir(adir) |
455 os.mkdir(adir) |
441 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit) |
456 covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit) |
442 |
457 |
443 class Timeout(Exception): |
|
444 pass |
|
445 |
|
446 def alarmed(signum, frame): |
|
447 raise Timeout |
|
448 |
|
449 def pytest(test, options, replacements): |
458 def pytest(test, options, replacements): |
450 py3kswitch = options.py3k_warnings and ' -3' or '' |
459 py3kswitch = options.py3k_warnings and ' -3' or '' |
451 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test) |
460 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test) |
452 vlog("# Running", cmd) |
461 vlog("# Running", cmd) |
453 return run(cmd, options, replacements) |
462 return run(cmd, options, replacements) |
601 output = fromchild.read() |
610 output = fromchild.read() |
602 ret = fromchild.close() |
611 ret = fromchild.close() |
603 if ret is None: |
612 if ret is None: |
604 ret = 0 |
613 ret = 0 |
605 else: |
614 else: |
606 proc = Popen4(cmd) |
615 proc = Popen4(cmd, options.timeout) |
607 def cleanup(): |
616 def cleanup(): |
608 os.kill(proc.pid, signal.SIGTERM) |
617 try: |
|
618 proc.terminate() |
|
619 except OSError: |
|
620 pass |
609 ret = proc.wait() |
621 ret = proc.wait() |
610 if ret == 0: |
622 if ret == 0: |
611 ret = signal.SIGTERM << 8 |
623 ret = signal.SIGTERM << 8 |
612 killdaemons() |
624 killdaemons() |
613 return ret |
625 return ret |
614 |
626 |
|
627 output = '' |
|
628 proc.tochild.close() |
|
629 |
615 try: |
630 try: |
616 output = '' |
|
617 proc.tochild.close() |
|
618 output = proc.fromchild.read() |
631 output = proc.fromchild.read() |
619 ret = proc.wait() |
|
620 if wifexited(ret): |
|
621 ret = os.WEXITSTATUS(ret) |
|
622 except Timeout: |
|
623 vlog('# Process %d timed out - killing it' % proc.pid) |
|
624 cleanup() |
|
625 ret = 'timeout' |
|
626 output += ("\n### Abort: timeout after %d seconds.\n" |
|
627 % options.timeout) |
|
628 except KeyboardInterrupt: |
632 except KeyboardInterrupt: |
629 vlog('# Handling keyboard interrupt') |
633 vlog('# Handling keyboard interrupt') |
630 cleanup() |
634 cleanup() |
631 raise |
635 raise |
|
636 |
|
637 ret = proc.wait() |
|
638 if wifexited(ret): |
|
639 ret = os.WEXITSTATUS(ret) |
|
640 |
|
641 if proc.timeout: |
|
642 ret = 'timeout' |
|
643 |
|
644 if ret: |
|
645 killdaemons() |
632 |
646 |
633 for s, r in replacements: |
647 for s, r in replacements: |
634 output = re.sub(s, r, output) |
648 output = re.sub(s, r, output) |
635 return ret, splitnewlines(output) |
649 return ret, splitnewlines(output) |
636 |
650 |
753 os.path.join(HGTMP, test) |
767 os.path.join(HGTMP, test) |
754 |
768 |
755 os.mkdir(testtmp) |
769 os.mkdir(testtmp) |
756 os.chdir(testtmp) |
770 os.chdir(testtmp) |
757 |
771 |
758 if options.timeout > 0: |
|
759 signal.alarm(options.timeout) |
|
760 |
|
761 ret, out = runner(testpath, options, [ |
772 ret, out = runner(testpath, options, [ |
762 (re.escape(testtmp), '$TESTTMP'), |
773 (re.escape(testtmp), '$TESTTMP'), |
763 (r':%s\b' % options.port, ':$HGPORT'), |
774 (r':%s\b' % options.port, ':$HGPORT'), |
764 (r':%s\b' % (options.port + 1), ':$HGPORT1'), |
775 (r':%s\b' % (options.port + 1), ':$HGPORT1'), |
765 (r':%s\b' % (options.port + 2), ':$HGPORT2'), |
776 (r':%s\b' % (options.port + 2), ':$HGPORT2'), |
766 ]) |
777 ]) |
767 vlog("# Ret was:", ret) |
778 vlog("# Ret was:", ret) |
768 |
|
769 if options.timeout > 0: |
|
770 signal.alarm(0) |
|
771 |
779 |
772 mark = '.' |
780 mark = '.' |
773 if ret == 0: |
781 if ret == 0: |
774 success() |
782 success() |
775 |
783 |
805 if failed: |
813 if failed: |
806 fail("hghave failed checking for %s" % failed[-1], ret) |
814 fail("hghave failed checking for %s" % failed[-1], ret) |
807 skipped = False |
815 skipped = False |
808 else: |
816 else: |
809 skip(missing[-1]) |
817 skip(missing[-1]) |
|
818 elif ret == 'timeout': |
|
819 mark = 't' |
|
820 fail("timed out", ret) |
810 elif out != refout: |
821 elif out != refout: |
811 mark = '!' |
822 mark = '!' |
812 if ret == 'timeout': |
823 if ret: |
813 fail("timed out", ret) |
|
814 elif ret: |
|
815 fail("output changed and returned error code %d" % ret, ret) |
824 fail("output changed and returned error code %d" % ret, ret) |
816 else: |
825 else: |
817 fail("output changed", ret) |
826 fail("output changed", ret) |
818 if ret != 'timeout' and not options.nodiff: |
827 if ret != 'timeout' and not options.nodiff: |
819 if options.view: |
828 if options.view: |