aboutsummaryrefslogtreecommitdiff
path: root/app/tests/test_backends_servers.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-09-10 00:16:00 -0400
committerhistoria <historiavg@proton.me>2026-09-10 00:16:00 -0400
commite2da233cd1baa859a5721542fc8b80d9f3f880e7 (patch)
tree4e86797cbd7dce0434feef65bc0ad2e97bfbe4c0 /app/tests/test_backends_servers.py
parent31459b281b6a5368c692b3c42c91e522995ebd57 (diff)
downloadtts-audiobook-generator-e2da233cd1baa859a5721542fc8b80d9f3f880e7.tar.gz
fix: smart chunking, extraction, and process-safety issuesHEADmain
Diffstat (limited to 'app/tests/test_backends_servers.py')
-rw-r--r--app/tests/test_backends_servers.py37
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, \