diff options
Diffstat (limited to 'Lib/test/test_signal.py')
-rw-r--r-- | Lib/test/test_signal.py | 61 |
1 files changed, 57 insertions, 4 deletions
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 2a6217e..80c0ff4 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -78,6 +78,48 @@ class PosixTests(unittest.TestCase): self.assertNotIn(signal.NSIG, s) self.assertLess(len(s), signal.NSIG) + @unittest.skipUnless(sys.executable, "sys.executable required.") + def test_keyboard_interrupt_exit_code(self): + """KeyboardInterrupt triggers exit via SIGINT.""" + process = subprocess.run( + [sys.executable, "-c", + "import os,signal; os.kill(os.getpid(), signal.SIGINT)"], + stderr=subprocess.PIPE) + self.assertIn(b"KeyboardInterrupt", process.stderr) + self.assertEqual(process.returncode, -signal.SIGINT) + + @unittest.skipUnless(sys.executable, "sys.executable required.") + def test_keyboard_interrupt_communicated_to_shell(self): + """KeyboardInterrupt exits such that shells detect a ^C.""" + try: + bash_proc = subprocess.run( + ["bash", "-c", 'echo "${BASH_VERSION}"'], + stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + except OSError: + raise unittest.SkipTest("bash required.") + if bash_proc.returncode: + raise unittest.SkipTest("could not determine bash version.") + bash_ver = bash_proc.stdout.decode("ascii").strip() + bash_major_minor = [int(n) for n in bash_ver.split(".", 2)[:2]] + if bash_major_minor < [4, 4]: + # In older versions of bash, -i does not work as needed + # _for this automated test_. Older shells do behave as + # expected in manual interactive use. + raise unittest.SkipTest(f"bash version {bash_ver} is too old.") + # The motivation for https://bugs.python.org/issue1054041. + # An _interactive_ shell (bash -i simulates that here) detects + # when a command exits via ^C and stops executing further + # commands. + process = subprocess.run( + ["bash", "-ic", + f"{sys.executable} -c 'import os,signal; os.kill(os.getpid(), signal.SIGINT)'; " + "echo TESTFAIL using bash \"${BASH_VERSION}\""], + stderr=subprocess.PIPE, stdout=subprocess.PIPE) + self.assertIn(b"KeyboardInterrupt", process.stderr) + # An interactive shell will abort if python exits properly to + # indicate that a KeyboardInterrupt occurred. + self.assertNotIn(b"TESTFAIL", process.stdout) + @unittest.skipUnless(sys.platform == "win32", "Windows specific") class WindowsSignalTests(unittest.TestCase): @@ -112,6 +154,20 @@ class WindowsSignalTests(unittest.TestCase): with self.assertRaises(ValueError): signal.signal(7, handler) + @unittest.skipUnless(sys.executable, "sys.executable required.") + def test_keyboard_interrupt_exit_code(self): + """KeyboardInterrupt triggers an exit using STATUS_CONTROL_C_EXIT.""" + # We don't test via os.kill(os.getpid(), signal.CTRL_C_EVENT) here + # as that requires setting up a console control handler in a child + # in its own process group. Doable, but quite complicated. (see + # @eryksun on https://github.com/python/cpython/pull/11862) + process = subprocess.run( + [sys.executable, "-c", "raise KeyboardInterrupt"], + stderr=subprocess.PIPE) + self.assertIn(b"KeyboardInterrupt", process.stderr) + STATUS_CONTROL_C_EXIT = 0xC000013A + self.assertEqual(process.returncode, STATUS_CONTROL_C_EXIT) + class WakeupFDTests(unittest.TestCase): @@ -1217,11 +1273,8 @@ class StressTest(unittest.TestCase): class RaiseSignalTest(unittest.TestCase): def test_sigint(self): - try: + with self.assertRaises(KeyboardInterrupt): signal.raise_signal(signal.SIGINT) - self.fail("Expected KeyInterrupt") - except KeyboardInterrupt: - pass @unittest.skipIf(sys.platform != "win32", "Windows specific test") def test_invalid_argument(self): |