diff options
Diffstat (limited to 'app/tests/test_backends_servers.py')
| -rw-r--r-- | app/tests/test_backends_servers.py | 37 |
1 files changed, 31 insertions, 6 deletions
diff --git a/app/tests/test_backends_servers.py b/app/tests/test_backends_servers.py index 9fd71f2..283df95 100644 --- a/app/tests/test_backends_servers.py +++ b/app/tests/test_backends_servers.py @@ -370,19 +370,44 @@ class ReapTests(unittest.TestCase): class KillPidTests(unittest.TestCase): - """_kill_pid: the reap check ends the grace wait before SIGKILL.""" + """_kill_pid: the group probe decides the wait's end, not the reap. - def test_reaped_child_ends_wait_without_sigkill(self): + Reaping the launcher proves nothing about the rest of its process + group: workers can outlive it and must still get the SIGKILL + escalation. + """ + + def test_empty_group_ends_wait_without_sigkill(self): + # Single-process server: the launcher is reaped and the group is + # empty (killpg(0) raises) — success immediately, no SIGKILL. with patch("os.getpgid", return_value=4242), \ - patch("os.killpg") as mk_killpg, \ - patch("os.waitpid", return_value=(4242, 0)) as mk_waitpid, \ + patch("os.killpg", + side_effect=[None, ProcessLookupError]) as mk_killpg, \ + patch("os.waitpid", return_value=(4242, 0)), \ patch("time.sleep") as mk_sleep: ok = servers._kill_pid(4242) self.assertTrue(ok) - mk_killpg.assert_called_once_with(4242, signal.SIGTERM) - mk_waitpid.assert_called_once_with(4242, os.WNOHANG) + self.assertEqual(mk_killpg.call_args_list[0].args, + (4242, signal.SIGTERM)) + self.assertEqual(mk_killpg.call_args_list[-1].args, (4242, 0)) mk_sleep.assert_not_called() + def test_reaped_leader_with_live_workers_escalates_to_sigkill(self): + # A launcher that dies on SIGTERM while its workers ignore it: + # reaping the leader must not end the stop, the surviving group + # keeps burning the grace period and then gets SIGKILLed. + with patch("os.getpgid", return_value=4242), \ + patch("os.killpg", return_value=None) as mk_killpg, \ + patch("os.waitpid", return_value=(4242, 0)), \ + patch("time.sleep"): + ok = servers._kill_pid(4242) + self.assertTrue(ok) + calls = mk_killpg.call_args_list + self.assertEqual(calls[0].args, (4242, signal.SIGTERM)) + self.assertEqual(calls[-1].args, (4242, signal.SIGKILL)) + # The group was probed repeatedly while waiting for the workers. + self.assertGreater(len(calls), 2) + def test_escalates_to_sigkill_when_child_stays_alive(self): with patch("os.getpgid", return_value=4242), \ patch("os.killpg") as mk_killpg, \ |
