11import subprocess
22import time
3+ import sys
34
45import pytest
56
1314from tests .helpers .settings import ARCHIVE_ENTRYPOINT , LATENT_LITE_ENTRYPOINT
1415
1516
17+ def wait_for_output (process , target_string , timeout = 60 ):
18+ """
19+ Wait for a specific string to appear in the subprocess stdout.
20+
21+ Args:
22+ process: subprocess.Popen object
23+ target_string: String to wait for in stdout
24+ timeout: Maximum time to wait in seconds
25+
26+ Returns:
27+ bool: True if string was found, False if timeout occurred
28+ """
29+ import time
30+ start_time = time .time ()
31+
32+ # Make stdout non-blocking on Unix systems
33+ if sys .platform != 'win32' :
34+ import fcntl
35+ import os
36+ flags = fcntl .fcntl (process .stdout , fcntl .F_GETFL )
37+ fcntl .fcntl (process .stdout , fcntl .F_SETFL , flags | os .O_NONBLOCK )
38+
39+ buffer = ""
40+ while time .time () - start_time < timeout :
41+ try :
42+ # Read available data
43+ chunk = process .stdout .read (1024 )
44+ if chunk :
45+ chunk_str = chunk .decode ('utf-8' , errors = 'ignore' )
46+ buffer += chunk_str
47+ print (chunk_str , end = '' , flush = True ) # Echo output for visibility
48+
49+ if target_string in buffer :
50+ return True
51+ else :
52+ # No data available, sleep briefly
53+ time .sleep (0.1 )
54+ except (BlockingIOError , TypeError ):
55+ # No data available yet
56+ time .sleep (0.1 )
57+
58+ # Check if process has terminated
59+ if process .poll () is not None :
60+ # Process ended, read remaining output
61+ remaining = process .stdout .read ()
62+ if remaining :
63+ remaining_str = remaining .decode ('utf-8' , errors = 'ignore' )
64+ print (remaining_str , end = '' , flush = True )
65+ if target_string in remaining_str :
66+ return True
67+ return False
68+
69+ return False
70+
71+
1672@pytest .fixture (scope = "function" )
1773def docker_containers ():
1874 processes = (
@@ -39,7 +95,10 @@ def single_local_chain():
3995
4096
4197def test_retry_sync_substrate (single_local_chain ):
42- time .sleep (10 )
98+ # Wait for the Docker container to be ready
99+ if not wait_for_output (single_local_chain .process , "Imported #1" , timeout = 60 ):
100+ raise TimeoutError ("Docker container did not start properly - 'Imported #1' not found in output" )
101+
43102 with RetrySyncSubstrate (
44103 single_local_chain .uri , fallback_chains = [LATENT_LITE_ENTRYPOINT ]
45104 ) as substrate :
@@ -58,7 +117,11 @@ def test_retry_sync_substrate(single_local_chain):
58117 "It does run locally, however."
59118)
60119def test_retry_sync_substrate_max_retries (docker_containers ):
61- time .sleep (10 )
120+ # Wait for both Docker containers to be ready
121+ for i , container in enumerate (docker_containers ):
122+ if not wait_for_output (container .process , "Imported #1" , timeout = 60 ):
123+ raise TimeoutError (f"Docker container { i } did not start properly - 'Imported #1' not found in output" )
124+
62125 with RetrySyncSubstrate (
63126 docker_containers [0 ].uri , fallback_chains = [docker_containers [1 ].uri ]
64127 ) as substrate :
0 commit comments