diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:26:05 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:26:05 +0000 |
commit | 14f1b3e8826ce43b978db93a62d1166055db5394 (patch) | |
tree | 0a00ad8d3498783fe0193f3b656bca17c4c8697d /packages/Python/lldbsuite/test/test_runner | |
parent | 4ee8c119c71a06dcad1e0fecc8c675e480e59337 (diff) |
Notes
Diffstat (limited to 'packages/Python/lldbsuite/test/test_runner')
-rw-r--r-- | packages/Python/lldbsuite/test/test_runner/process_control.py | 62 | ||||
-rwxr-xr-x | packages/Python/lldbsuite/test/test_runner/test/test_process_control.py | 13 |
2 files changed, 57 insertions, 18 deletions
diff --git a/packages/Python/lldbsuite/test/test_runner/process_control.py b/packages/Python/lldbsuite/test/test_runner/process_control.py index a7e639e4b8b65..720f5112a4cd8 100644 --- a/packages/Python/lldbsuite/test/test_runner/process_control.py +++ b/packages/Python/lldbsuite/test/test_runner/process_control.py @@ -23,6 +23,7 @@ import threading class CommunicatorThread(threading.Thread): """Provides a thread class that communicates with a subprocess.""" + def __init__(self, process, event, output_file): super(CommunicatorThread, self).__init__() # Don't let this thread prevent shutdown. @@ -100,6 +101,7 @@ class ProcessHelper(object): @see ProcessHelper.process_helper() """ + def __init__(self): super(ProcessHelper, self).__init__() @@ -281,6 +283,7 @@ class UnixProcessHelper(ProcessHelper): This implementation supports anything that looks Posix-y (e.g. Darwin, Linux, *BSD, etc.) """ + def __init__(self): super(UnixProcessHelper, self).__init__() @@ -302,7 +305,7 @@ class UnixProcessHelper(ProcessHelper): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True, # Elicits automatic byte -> string decoding in Py3 + universal_newlines=True, # Elicits automatic byte -> string decoding in Py3 close_fds=True, preexec_fn=preexec_func) @@ -357,18 +360,28 @@ class UnixProcessHelper(ProcessHelper): # Choose kill mechanism based on whether we're targeting # a process group or just a process. - if popen_process.using_process_groups: - # if log_file: - # log_file.write( - # "sending signum {} to process group {} now\n".format( - # signum, popen_process.pid)) - os.killpg(popen_process.pid, signum) - else: - # if log_file: - # log_file.write( - # "sending signum {} to process {} now\n".format( - # signum, popen_process.pid)) - os.kill(popen_process.pid, signum) + try: + if popen_process.using_process_groups: + # if log_file: + # log_file.write( + # "sending signum {} to process group {} now\n".format( + # signum, popen_process.pid)) + os.killpg(popen_process.pid, signum) + else: + # if log_file: + # log_file.write( + # "sending signum {} to process {} now\n".format( + # signum, popen_process.pid)) + os.kill(popen_process.pid, signum) + except OSError as error: + import errno + if error.errno == errno.ESRCH: + # This is okay - failed to find the process. It may be that + # that the timeout pre-kill hook eliminated the process. We'll + # ignore. + pass + else: + raise def soft_terminate(self, popen_process, log_file=None, want_core=True): # Choose signal based on desire for core file. @@ -412,8 +425,10 @@ class UnixProcessHelper(ProcessHelper): signal_name = signal_names_by_number.get(signo, "") return (signo, signal_name) + class WindowsProcessHelper(ProcessHelper): """Provides a Windows implementation of the ProcessHelper class.""" + def __init__(self): super(WindowsProcessHelper, self).__init__() @@ -429,7 +444,7 @@ class WindowsProcessHelper(ProcessHelper): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True, # Elicits automatic byte -> string decoding in Py3 + universal_newlines=True, # Elicits automatic byte -> string decoding in Py3 creationflags=creation_flags) def was_hard_terminate(self, returncode): @@ -447,6 +462,7 @@ class ProcessDriver(object): way. The on_process_exited method is informed if the exit was natural or if it was due to a timeout. """ + def __init__(self, soft_terminate_timeout=10.0): super(ProcessDriver, self).__init__() self.process_helper = ProcessHelper.process_helper() @@ -477,6 +493,19 @@ class ProcessDriver(object): def on_process_exited(self, command, output, was_timeout, exit_status): pass + def on_timeout_pre_kill(self): + """Called after the timeout interval elapses but before killing it. + + This method is added to enable derived classes the ability to do + something to the process prior to it being killed. For example, + this would be a good spot to run a program that samples the process + to see what it was doing (or not doing). + + Do not attempt to reap the process (i.e. use wait()) in this method. + That will interfere with the kill mechanism and return code processing. + """ + pass + def write(self, content): # pylint: disable=no-self-use # Intended - we want derived classes to be able to override @@ -634,6 +663,11 @@ class ProcessDriver(object): # Reap the child process here. self.returncode = self.process.wait() else: + + # Allow derived classes to do some work after we detected + # a timeout but before we touch the timed-out process. + self.on_timeout_pre_kill() + # Prepare to stop the process process_terminated = completed_normally terminate_attempt_count = 0 diff --git a/packages/Python/lldbsuite/test/test_runner/test/test_process_control.py b/packages/Python/lldbsuite/test/test_runner/test/test_process_control.py index 817c83c4fb55f..88ad961be2b9c 100755 --- a/packages/Python/lldbsuite/test/test_runner/test/test_process_control.py +++ b/packages/Python/lldbsuite/test/test_runner/test/test_process_control.py @@ -27,6 +27,7 @@ from test_runner import process_control class TestInferiorDriver(process_control.ProcessDriver): + def __init__(self, soft_terminate_timeout=None): super(TestInferiorDriver, self).__init__( soft_terminate_timeout=soft_terminate_timeout) @@ -58,6 +59,7 @@ class TestInferiorDriver(process_control.ProcessDriver): class ProcessControlTests(unittest.TestCase): + @classmethod def _suppress_soft_terminate(cls, command): # Do the right thing for your platform here. @@ -79,7 +81,8 @@ class ProcessControlTests(unittest.TestCase): # Base command. script_name = "{}/inferior.py".format(os.path.dirname(__file__)) if not os.path.exists(script_name): - raise Exception("test inferior python script not found: {}".format(script_name)) + raise Exception( + "test inferior python script not found: {}".format(script_name)) command = ([sys.executable, script_name]) if ignore_soft_terminate: @@ -97,6 +100,7 @@ class ProcessControlTests(unittest.TestCase): class ProcessControlNoTimeoutTests(ProcessControlTests): """Tests the process_control module.""" + def test_run_completes(self): """Test that running completes and gets expected stdout/stderr.""" driver = TestInferiorDriver() @@ -115,6 +119,7 @@ class ProcessControlNoTimeoutTests(ProcessControlTests): class ProcessControlTimeoutTests(ProcessControlTests): + def test_run_completes(self): """Test that running completes and gets expected return code.""" driver = TestInferiorDriver() @@ -124,7 +129,7 @@ class ProcessControlTimeoutTests(ProcessControlTests): "{}s".format(timeout_seconds), False) self.assertTrue( - driver.completed_event.wait(2*timeout_seconds), + driver.completed_event.wait(2 * timeout_seconds), "process failed to complete") self.assertEqual(driver.returncode, 0) @@ -141,13 +146,13 @@ class ProcessControlTimeoutTests(ProcessControlTests): # Sleep twice as long as the timeout interval. This # should force a timeout. self.inferior_command( - options="--sleep {}".format(timeout_seconds*2)), + options="--sleep {}".format(timeout_seconds * 2)), "{}s".format(timeout_seconds), with_core) # We should complete, albeit with a timeout. self.assertTrue( - driver.completed_event.wait(2*timeout_seconds), + driver.completed_event.wait(2 * timeout_seconds), "process failed to complete") # Ensure we received a timeout. |