|
4 | 4 | import io |
5 | 5 | import marshal |
6 | 6 | import os |
| 7 | +import socket |
7 | 8 | import subprocess |
8 | 9 | import sys |
9 | 10 | import tempfile |
10 | | -import time |
11 | 11 | import unittest |
12 | 12 | from unittest import mock |
13 | 13 |
|
|
17 | 17 | ) |
18 | 18 |
|
19 | 19 | from test.support.os_helper import unlink |
20 | | -from test.support import force_not_colorized_test_class |
| 20 | +from test.support import force_not_colorized_test_class, SHORT_TIMEOUT |
| 21 | +from test.support.socket_helper import find_unused_port |
21 | 22 |
|
22 | 23 | PROCESS_VM_READV_SUPPORTED = False |
23 | 24 |
|
@@ -47,18 +48,47 @@ def __repr__(self): |
47 | 48 |
|
48 | 49 |
|
49 | 50 | @contextlib.contextmanager |
50 | | -def test_subprocess(script, startup_delay=0.1): |
| 51 | +def test_subprocess(script): |
| 52 | + # Find an unused port for socket communication |
| 53 | + port = find_unused_port() |
| 54 | + |
| 55 | + # Inject socket connection code at the beginning of the script |
| 56 | + socket_code = f''' |
| 57 | +import socket |
| 58 | +_test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 59 | +_test_sock.connect(('localhost', {port})) |
| 60 | +_test_sock.sendall(b"ready") |
| 61 | +''' |
| 62 | + |
| 63 | + # Combine socket code with user script |
| 64 | + full_script = socket_code + script |
| 65 | + |
| 66 | + # Create server socket to wait for process to be ready |
| 67 | + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 68 | + server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
| 69 | + server_socket.bind(("localhost", port)) |
| 70 | + server_socket.settimeout(SHORT_TIMEOUT) |
| 71 | + server_socket.listen(1) |
| 72 | + |
51 | 73 | proc = subprocess.Popen( |
52 | | - [sys.executable, "-c", script], |
| 74 | + [sys.executable, "-c", full_script], |
53 | 75 | stdout=subprocess.DEVNULL, |
54 | 76 | stderr=subprocess.DEVNULL, |
55 | 77 | ) |
56 | 78 |
|
| 79 | + client_socket = None |
57 | 80 | try: |
58 | | - if startup_delay > 0: |
59 | | - time.sleep(startup_delay) |
| 81 | + # Wait for process to connect and send ready signal |
| 82 | + client_socket, _ = server_socket.accept() |
| 83 | + server_socket.close() |
| 84 | + response = client_socket.recv(1024) |
| 85 | + if response != b"ready": |
| 86 | + raise RuntimeError(f"Unexpected response from subprocess: {response}") |
| 87 | + |
60 | 88 | yield proc |
61 | 89 | finally: |
| 90 | + if client_socket is not None: |
| 91 | + client_socket.close() |
62 | 92 | if proc.poll() is None: |
63 | 93 | proc.kill() |
64 | 94 | proc.wait() |
@@ -1604,23 +1634,25 @@ def test_invalid_output_format_with_mocked_profiler(self): |
1604 | 1634 | ) |
1605 | 1635 |
|
1606 | 1636 | def test_is_process_running(self): |
1607 | | - with (test_subprocess("import time; time.sleep(0.2)") as proc, |
| 1637 | + with (test_subprocess("import time; time.sleep(1000)") as proc, |
1608 | 1638 | mock.patch("_remote_debugging.RemoteUnwinder") as mock_unwinder_class): |
1609 | 1639 | mock_unwinder_class.return_value = mock.MagicMock() |
1610 | 1640 | profiler = SampleProfiler(pid=proc.pid, sample_interval_usec=1000, all_threads=False) |
1611 | | - |
1612 | 1641 | self.assertTrue(profiler._is_process_running()) |
| 1642 | + proc.kill() |
1613 | 1643 |
|
1614 | 1644 | # Exit the context manager to ensure the process is terminated |
1615 | 1645 | self.assertFalse(profiler._is_process_running()) |
1616 | 1646 |
|
1617 | 1647 | @unittest.skipUnless(sys.platform == "linux", "Only valid on Linux") |
1618 | 1648 | def test_esrch_signal_handling(self): |
1619 | | - with test_subprocess("import time; time.sleep(0.1)") as proc: |
| 1649 | + with test_subprocess("import time; time.sleep(1000)") as proc: |
1620 | 1650 | unwinder = _remote_debugging.RemoteUnwinder(proc.pid) |
1621 | 1651 | initial_trace = unwinder.get_stack_trace() |
1622 | 1652 | self.assertIsNotNone(initial_trace) |
1623 | 1653 |
|
| 1654 | + proc.kill() |
| 1655 | + |
1624 | 1656 | # Wait for the process to die and try to get another trace |
1625 | 1657 | proc.wait() |
1626 | 1658 |
|
|
0 commit comments