diff --git a/scripts/test-guard-step.py b/scripts/test-guard-step.py index e11b67e..34f2ea4 100644 --- a/scripts/test-guard-step.py +++ b/scripts/test-guard-step.py @@ -13,6 +13,19 @@ ROOT = Path(__file__).resolve().parents[1] GUARD = ROOT / 'ci/guard-step.py' +def alive(pid): + """进程还在运行。已经被杀但没人回收的僵尸进程(CI 容器里没有 init)不算。""" + try: + os.kill(pid, 0) + except ProcessLookupError: + return False + try: + state = Path(f'/proc/{pid}/stat').read_text().rsplit(')', 1)[1].split()[0] + except (OSError, IndexError): + return True + return state != 'Z' + + class GuardStepTests(unittest.TestCase): def setUp(self): self.directory = tempfile.TemporaryDirectory() @@ -57,8 +70,7 @@ class GuardStepTests(unittest.TestCase): step.send_signal(signal.SIGTERM) self.assertEqual(step.wait(timeout=30), 128 + signal.SIGTERM) time.sleep(0.5) - with self.assertRaises(ProcessLookupError): - os.kill(child, 0) + self.assertFalse(alive(child)) self.assertEqual(self.guard('next', 30, 'true', stderr=subprocess.DEVNULL).wait(timeout=20), 125) def test_a_background_process_left_by_a_finished_step_is_killed(self): @@ -66,8 +78,7 @@ class GuardStepTests(unittest.TestCase): step = self.guard('leaky', 30, 'bash', '-c', f'sleep 120 & echo $! > {pid_file}', stderr=subprocess.DEVNULL) self.assertEqual(step.wait(timeout=20), 0) time.sleep(0.5) - with self.assertRaises(ProcessLookupError): - os.kill(int(pid_file.read_text()), 0) + self.assertFalse(alive(int(pid_file.read_text()))) def test_the_pipeline_deadline_is_the_last_resort(self): self.env['MOSHIRO_PIPELINE_DEADLINE'] = '2'