| # Copyright 2026 The Fuchsia Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| import os | |
| import signal | |
| import subprocess | |
| import sys | |
| import time | |
| import unittest | |
| class TestInterruption(unittest.TestCase): | |
| def _run_test_executable_and_send_signal( | |
| self, sig: signal.Signals | |
| ) -> subprocess.Popen[str]: | |
| code = """ | |
| import fuchsia_controller_py | |
| import sys | |
| import time | |
| try: | |
| # _block_forever blocks forever but should be interruptible by SIGINT. | |
| fuchsia_controller_py._block_forever() | |
| except KeyboardInterrupt: | |
| print("Caught KeyboardInterrupt") | |
| sys.exit(0) | |
| except Exception as e: | |
| print(f"Caught exception: {e}") | |
| sys.exit(1) | |
| print("Exited without exception") | |
| sys.exit(2) | |
| """ | |
| env = os.environ.copy() | |
| env["PYTHONPATH"] = ":".join(sys.path) | |
| p = subprocess.Popen( | |
| [sys.executable, "-c", code], | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.PIPE, | |
| text=True, | |
| env=env, | |
| ) | |
| time.sleep(5) | |
| p.send_signal(sig) | |
| try: | |
| stdout, stderr = p.communicate(timeout=5) | |
| except subprocess.TimeoutExpired: | |
| p.kill() | |
| stdout, stderr = p.communicate() | |
| self.fail( | |
| f"Subprocess timed out after {str(sig)}. stderr: {stderr}, stdout: {stdout}" | |
| ) | |
| self.assertTrue( | |
| p.returncode == 0 or p.returncode == -sig, | |
| f"Subprocess failed with exit code {p.returncode}. stderr: {stderr}, stdout: {stdout}", | |
| ) | |
| self.assertTrue( | |
| "Caught KeyboardInterrupt" in stdout | |
| or "KeyboardInterrupt" in stderr, | |
| f"Expected KeyboardInterrupt in output. stderr: {stderr}, stdout: {stdout}", | |
| ) | |
| return p | |
| def test_block_forever_interruptible_sigint(self) -> None: | |
| self._run_test_executable_and_send_signal(signal.SIGINT) | |
| def test_block_forever_interruptible_sigterm(self) -> None: | |
| self._run_test_executable_and_send_signal(signal.SIGTERM) | |
| if __name__ == "__main__": | |
| unittest.main() |