import io import os import select import signal import subprocess import sys import unittest from unittest.mock import patch from car_traker import container_entrypoint class ContainerEntrypointTests(unittest.TestCase): def test_requires_a_command(self): with patch("sys.argv", ["entrypoint"]), patch("sys.stderr", new_callable=io.StringIO): self.assertEqual(container_entrypoint.main(), 3) @unittest.skipUnless(os.environ.get("RUN_BROWSER_TESTS") == "1", "Xvfb container tests are opt-in") class VirtualDisplayTests(unittest.TestCase): def test_child_exit_code_is_preserved(self): result = subprocess.run([ sys.executable, "-m", "car_traker.container_entrypoint", sys.executable, "-c", "raise SystemExit(7)", ], capture_output=True, text=True, timeout=20) self.assertEqual(result.returncode, 7, result.stderr) def test_sigterm_waits_for_child_finalization(self): command = """ import signal, sys, time def stop(signum, frame): print('cleanup-start', flush=True) time.sleep(1) print('cleanup-done', flush=True) sys.exit(130) signal.signal(signal.SIGTERM, stop) print('ready', flush=True) while True: signal.pause() """ process = subprocess.Popen([ sys.executable, "-m", "car_traker.container_entrypoint", sys.executable, "-c", command, ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) try: ready, _, _ = select.select([process.stdout], [], [], 15) self.assertTrue(ready, "Child did not become ready") self.assertEqual(process.stdout.readline().strip(), "ready") process.send_signal(signal.SIGTERM) stdout, stderr = process.communicate(timeout=15) self.assertEqual(process.returncode, 130, stderr) self.assertIn("cleanup-start", stdout) self.assertIn("cleanup-done", stdout) finally: if process.poll() is None: process.terminate() process.communicate(timeout=15)