73 # subprocess._cleanup can race with any Popen.wait or Popen.poll on py24 |
73 # subprocess._cleanup can race with any Popen.wait or Popen.poll on py24 |
74 # http://bugs.python.org/issue1731717 for details. We shouldn't be producing |
74 # http://bugs.python.org/issue1731717 for details. We shouldn't be producing |
75 # zombies but it's pretty harmless even if we do. |
75 # zombies but it's pretty harmless even if we do. |
76 if sys.version_info < (2, 5): |
76 if sys.version_info < (2, 5): |
77 subprocess._cleanup = lambda: None |
77 subprocess._cleanup = lambda: None |
|
78 |
|
79 wifexited = getattr(os, "WIFEXITED", lambda x: False) |
78 |
80 |
79 closefds = os.name == 'posix' |
81 closefds = os.name == 'posix' |
80 def Popen4(cmd, wd, timeout, env=None): |
82 def Popen4(cmd, wd, timeout, env=None): |
81 processlock.acquire() |
83 processlock.acquire() |
82 p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd, env=env, |
84 p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd, env=env, |
718 def fail(self, msg): |
720 def fail(self, msg): |
719 # unittest differentiates between errored and failed. |
721 # unittest differentiates between errored and failed. |
720 # Failed is denoted by AssertionError (by default at least). |
722 # Failed is denoted by AssertionError (by default at least). |
721 raise AssertionError(msg) |
723 raise AssertionError(msg) |
722 |
724 |
|
725 def _runcommand(self, cmd, wd, replacements, env, debug=False, |
|
726 timeout=None): |
|
727 """Run command in a sub-process, capturing the output (stdout and |
|
728 stderr). |
|
729 |
|
730 Return a tuple (exitcode, output). output is None in debug mode. |
|
731 """ |
|
732 if debug: |
|
733 proc = subprocess.Popen(cmd, shell=True, cwd=wd, env=env) |
|
734 ret = proc.wait() |
|
735 return (ret, None) |
|
736 |
|
737 proc = Popen4(cmd, wd, timeout, env) |
|
738 def cleanup(): |
|
739 terminate(proc) |
|
740 ret = proc.wait() |
|
741 if ret == 0: |
|
742 ret = signal.SIGTERM << 8 |
|
743 killdaemons(env['DAEMON_PIDS']) |
|
744 return ret |
|
745 |
|
746 output = '' |
|
747 proc.tochild.close() |
|
748 |
|
749 try: |
|
750 output = proc.fromchild.read() |
|
751 except KeyboardInterrupt: |
|
752 vlog('# Handling keyboard interrupt') |
|
753 cleanup() |
|
754 raise |
|
755 |
|
756 ret = proc.wait() |
|
757 if wifexited(ret): |
|
758 ret = os.WEXITSTATUS(ret) |
|
759 |
|
760 if proc.timeout: |
|
761 ret = 'timeout' |
|
762 |
|
763 if ret: |
|
764 killdaemons(env['DAEMON_PIDS']) |
|
765 |
|
766 for s, r in replacements: |
|
767 output = re.sub(s, r, output) |
|
768 return ret, output.splitlines(True) |
|
769 |
723 class PythonTest(Test): |
770 class PythonTest(Test): |
724 """A Python-based test.""" |
771 """A Python-based test.""" |
725 |
772 |
726 @property |
773 @property |
727 def refpath(self): |
774 def refpath(self): |
731 py3kswitch = self._py3kwarnings and ' -3' or '' |
778 py3kswitch = self._py3kwarnings and ' -3' or '' |
732 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, self.path) |
779 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, self.path) |
733 vlog("# Running", cmd) |
780 vlog("# Running", cmd) |
734 if os.name == 'nt': |
781 if os.name == 'nt': |
735 replacements.append((r'\r\n', '\n')) |
782 replacements.append((r'\r\n', '\n')) |
736 result = run(cmd, self._testtmp, replacements, env, |
783 result = self._runcommand(cmd, self._testtmp, replacements, env, |
737 debug=self._debug, timeout=self._timeout) |
784 debug=self._debug, timeout=self._timeout) |
738 if self._aborted: |
785 if self._aborted: |
739 raise KeyboardInterrupt() |
786 raise KeyboardInterrupt() |
740 |
787 |
741 return result |
788 return result |
742 |
789 |
779 f.close() |
826 f.close() |
780 |
827 |
781 cmd = '%s "%s"' % (self._shell, fname) |
828 cmd = '%s "%s"' % (self._shell, fname) |
782 vlog("# Running", cmd) |
829 vlog("# Running", cmd) |
783 |
830 |
784 exitcode, output = run(cmd, self._testtmp, replacements, env, |
831 exitcode, output = self._runcommand(cmd, self._testtmp, replacements, |
785 debug=self._debug, timeout=self._timeout) |
832 env, debug=self._debug, |
|
833 timeout=self._timeout) |
786 |
834 |
787 if self._aborted: |
835 if self._aborted: |
788 raise KeyboardInterrupt() |
836 raise KeyboardInterrupt() |
789 |
837 |
790 # Do not merge output if skipped. Return hghave message instead. |
838 # Do not merge output if skipped. Return hghave message instead. |
1073 |
1121 |
1074 @staticmethod |
1122 @staticmethod |
1075 def _stringescape(s): |
1123 def _stringescape(s): |
1076 return TTest.ESCAPESUB(TTest._escapef, s) |
1124 return TTest.ESCAPESUB(TTest._escapef, s) |
1077 |
1125 |
1078 |
|
1079 wifexited = getattr(os, "WIFEXITED", lambda x: False) |
|
1080 def run(cmd, wd, replacements, env, debug=False, timeout=None): |
|
1081 """Run command in a sub-process, capturing the output (stdout and stderr). |
|
1082 Return a tuple (exitcode, output). output is None in debug mode.""" |
|
1083 if debug: |
|
1084 proc = subprocess.Popen(cmd, shell=True, cwd=wd, env=env) |
|
1085 ret = proc.wait() |
|
1086 return (ret, None) |
|
1087 |
|
1088 proc = Popen4(cmd, wd, timeout, env) |
|
1089 def cleanup(): |
|
1090 terminate(proc) |
|
1091 ret = proc.wait() |
|
1092 if ret == 0: |
|
1093 ret = signal.SIGTERM << 8 |
|
1094 killdaemons(env['DAEMON_PIDS']) |
|
1095 return ret |
|
1096 |
|
1097 output = '' |
|
1098 proc.tochild.close() |
|
1099 |
|
1100 try: |
|
1101 output = proc.fromchild.read() |
|
1102 except KeyboardInterrupt: |
|
1103 vlog('# Handling keyboard interrupt') |
|
1104 cleanup() |
|
1105 raise |
|
1106 |
|
1107 ret = proc.wait() |
|
1108 if wifexited(ret): |
|
1109 ret = os.WEXITSTATUS(ret) |
|
1110 |
|
1111 if proc.timeout: |
|
1112 ret = 'timeout' |
|
1113 |
|
1114 if ret: |
|
1115 killdaemons(env['DAEMON_PIDS']) |
|
1116 |
|
1117 for s, r in replacements: |
|
1118 output = re.sub(s, r, output) |
|
1119 return ret, output.splitlines(True) |
|
1120 |
|
1121 iolock = threading.RLock() |
1126 iolock = threading.RLock() |
1122 |
1127 |
1123 class SkipTest(Exception): |
1128 class SkipTest(Exception): |
1124 """Raised to indicate that a test is to be skipped.""" |
1129 """Raised to indicate that a test is to be skipped.""" |
1125 |
1130 |