diff --git a/testing/mozbase/mozprocess/mozprocess/processhandler.py b/testing/mozbase/mozprocess/mozprocess/processhandler.py index 4721e011b1f..5ec7f153aab 100644 --- a/testing/mozbase/mozprocess/mozprocess/processhandler.py +++ b/testing/mozbase/mozprocess/mozprocess/processhandler.py @@ -110,7 +110,7 @@ class ProcessHandlerMixin(object): else: subprocess.Popen.__del__(self) - def kill(self): + def kill(self, sig=None): self.returncode = 0 if isWin: if not self._ignore_children and self._handle and self._job: @@ -126,20 +126,18 @@ class ProcessHandlerMixin(object): self._cleanup() if err is not None: raise OSError(err) - else: - pass else: + sig = sig or signal.SIGKILL if not self._ignore_children: try: - os.killpg(self.pid, signal.SIGKILL) + os.killpg(self.pid, sig) except BaseException, e: if getattr(e, "errno", None) != 3: # Error 3 is "no such process", which is ok print >> sys.stdout, "Could not kill process, could not find pid: %s, assuming it's already dead" % self.pid else: - os.kill(self.pid, signal.SIGKILL) - if self.returncode is None: - self.returncode = subprocess.Popen._internal_poll(self) + os.kill(self.pid, sig) + self.returncode = -sig self._cleanup() return self.returncode @@ -417,7 +415,7 @@ falling back to not using job objects for managing child processes""" threadalive = self._procmgrthread.is_alive() else: threadalive = self._procmgrthread.isAlive() - if self._job and threadalive: + if self._job and threadalive: # Then we are managing with IO Completion Ports # wait on a signal so we know when we have seen the last # process come through. @@ -643,7 +641,7 @@ falling back to not using job objects for managing child processes""" self.processOutput(timeout=timeout, outputTimeout=outputTimeout) - def kill(self): + def kill(self, sig=None): """ Kills the managed process. @@ -654,9 +652,12 @@ falling back to not using job objects for managing child processes""" Note that this does not manage any state, save any output etc, it immediately kills the process. + + :param sig: Signal used to kill the process, defaults to SIGKILL + (has no effect on Windows) """ try: - return self.proc.kill() + return self.proc.kill(sig=sig) except AttributeError: # Try to print a relevant error message. if not self.proc: diff --git a/testing/mozbase/mozprocess/tests/test_mozprocess_kill.py b/testing/mozbase/mozprocess/tests/test_mozprocess_kill.py index c2466d99c23..bb04ab5ceac 100644 --- a/testing/mozbase/mozprocess/tests/test_mozprocess_kill.py +++ b/testing/mozbase/mozprocess/tests/test_mozprocess_kill.py @@ -23,7 +23,8 @@ class ProcTestKill(proctest.ProcTest): self.determine_status(detected, output, p.proc.returncode, - p.didTimeout) + p.didTimeout, + expectedfail=('returncode',)) def test_process_kill_deep(self): """Process is started, we kill it, we use a deep process tree""" @@ -37,7 +38,8 @@ class ProcTestKill(proctest.ProcTest): self.determine_status(detected, output, p.proc.returncode, - p.didTimeout) + p.didTimeout, + expectedfail=('returncode',)) def test_process_kill_deep_wait(self): """Process is started, we use a deep process tree, we let it spawn @@ -54,7 +56,8 @@ class ProcTestKill(proctest.ProcTest): self.determine_status(detected, output, p.proc.returncode, - p.didTimeout) + p.didTimeout, + expectedfail=('returncode',)) def test_process_kill_broad(self): """Process is started, we kill it, we use a broad process tree""" @@ -68,7 +71,8 @@ class ProcTestKill(proctest.ProcTest): self.determine_status(detected, output, p.proc.returncode, - p.didTimeout) + p.didTimeout, + expectedfail=('returncode',)) if __name__ == '__main__': unittest.main() diff --git a/testing/mozbase/mozprocess/tests/test_mozprocess_kill_broad_wait.py b/testing/mozbase/mozprocess/tests/test_mozprocess_kill_broad_wait.py index c3e4584c124..7d666c58eb0 100644 --- a/testing/mozbase/mozprocess/tests/test_mozprocess_kill_broad_wait.py +++ b/testing/mozbase/mozprocess/tests/test_mozprocess_kill_broad_wait.py @@ -29,7 +29,8 @@ class ProcTestKill(proctest.ProcTest): self.determine_status(detected, output, p.proc.returncode, - p.didTimeout) + p.didTimeout, + expectedfail=('returncode',)) if __name__ == '__main__': unittest.main() diff --git a/testing/mozbase/mozrunner/mozrunner/runner.py b/testing/mozbase/mozrunner/mozrunner/runner.py index aa2626222f8..505691d80bd 100755 --- a/testing/mozbase/mozrunner/mozrunner/runner.py +++ b/testing/mozbase/mozrunner/mozrunner/runner.py @@ -70,17 +70,25 @@ class Runner(object): """ Wait for the process to exit. Returns the process return code if the process exited, - returns None otherwise. + returns - if the process was killed (Unix only) + returns None if the process is still running. - If timeout is not None, will return after timeout seconds. - Use is_running() to determine whether or not a timeout occured. - Timeout is ignored if interactive was set to True. + :param timeout: if not None, will return after timeout seconds. + Use is_running() to determine whether or not a + timeout occured. Timeout is ignored if + interactive was set to True. """ if self.process_handler is not None: if isinstance(self.process_handler, subprocess.Popen): self.returncode = self.process_handler.wait() else: self.process_handler.wait(timeout) + + if not self.process_handler: + # the process was killed by another thread + return self.returncode + + # the process terminated, retrieve the return code self.returncode = self.process_handler.proc.poll() if self.returncode is not None: self.process_handler = None @@ -96,13 +104,16 @@ class Runner(object): return self.process_handler is not None - def stop(self): + def stop(self, sig=None): """ Kill the process + + :param sig: Signal used to kill the process, defaults to SIGKILL + (has no effect on Windows). """ if self.process_handler is None: return - self.process_handler.kill() + self.returncode = self.process_handler.kill(sig=sig) self.process_handler = None def reset(self):