@@ -96,7 +96,7 @@ def setup_test_app(self, launch_manager_daemon: dict[str, Any], version: str) ->
9696 app_target = "@score_lifecycle_health//examples/cpp_supervised_app:cpp_supervised_app"
9797 app_name = "cpp_supervised_app"
9898
99- app_binary = get_binary_path (app_target , version )
99+ app_binary = get_binary_path (app_target )
100100
101101 # Copy to daemon bin directory
102102 dest_path = bin_dir / app_name
@@ -150,16 +150,12 @@ def test_supervised_app_launches(
150150 )
151151 print (f"\n Daemon reported supervised process PID events: { ', ' .join (pid_events [:5 ])} " )
152152
153- # Verify daemon is still running
153+ # Verify daemon is still running and target app appears in supervision logs.
154154 assert daemon .is_running (), "Launch Manager daemon stopped unexpectedly"
155-
156- # Check daemon logs for supervision activity
157155 logs = daemon .get_logs ()
158- print (f"\n Daemon logs:\n { logs } " )
159-
160- # Verify logs show component was started
161- # (Actual log messages depend on Launch Manager implementation)
162- assert len (logs ) > 0 , "No daemon logs generated"
156+ assert app_name in logs or result .returncode == 0 , (
157+ f"No target-specific supervision evidence for { app_name } .\n Daemon logs:\n { logs } "
158+ )
163159
164160 def test_supervised_app_recovery (
165161 self , launch_manager_daemon : dict [str , Any ], setup_test_app : Path , version : str
@@ -195,36 +191,40 @@ def test_supervised_app_recovery(
195191 print (f"\n Killing supervised app (PID: { pid } )" )
196192 subprocess .run (["kill" , "-9" , pid ], check = True )
197193
198- # Wait for daemon to detect failure and restart
199- time .sleep (2.0 )
200-
201- # Verify app was restarted
202- result = subprocess .run (
203- ["pgrep" , "-f" , app_name ],
204- capture_output = True ,
205- text = True ,
206- check = False ,
207- )
208-
209- if result .returncode == 0 :
210- new_pid = result .stdout .strip ().split ("\n " )[0 ]
194+ # Poll for restart to avoid fixed-sleep race conditions.
195+ deadline = time .time () + 12.0
196+ new_pid = None
197+ while time .time () < deadline :
198+ result = subprocess .run (
199+ ["pgrep" , "-f" , app_name ],
200+ capture_output = True ,
201+ text = True ,
202+ check = False ,
203+ )
204+ if result .returncode == 0 :
205+ candidates = [p for p in result .stdout .strip ().split ("\n " ) if p and p != pid ]
206+ if candidates :
207+ new_pid = candidates [0 ]
208+ break
209+ time .sleep (0.25 )
210+
211+ if new_pid is not None :
211212 print (f"App restarted with new PID: { new_pid } " )
212- assert new_pid != pid , "PID should be different after restart"
213213 else :
214214 logs = daemon .get_logs ()
215215 pytest .fail (
216- "Supervised app was not found after forced kill; expected daemon recovery restart ."
217- f"\n Daemon logs:\n { logs } "
216+ "Supervised app was not restarted within timeout after forced kill."
217+ f"\n Target app: { app_name } \ n Daemon logs:\n { logs } "
218218 )
219219 else :
220220 logs = daemon .get_logs ()
221- recovery_signals = [
222- f"unexpected termination of process" ,
223- "Activating Recovery state." ,
224- f"Got kRunning timeout for process" ,
221+ target_patterns = [
222+ rf"unexpected termination of process.*\(\s*{ re .escape (app_name )} \s*\)" ,
223+ rf"Got kRunning timeout for process.*\(\s*{ re .escape (app_name )} \s*\)" ,
225224 ]
226- assert any (signal in logs for signal in recovery_signals ), (
227- f"Supervised app { app_name } not running and no recovery diagnostics found.\n Daemon logs:\n { logs } "
225+ assert any (re .search (pattern , logs ) for pattern in target_patterns ), (
226+ f"Supervised app { app_name } not running and no target-specific recovery diagnostics found."
227+ f"\n Daemon logs:\n { logs } "
228228 )
229229
230230 except subprocess .CalledProcessError as e :
@@ -264,16 +264,42 @@ def test_watchdog_detection(self, launch_manager_daemon: dict[str, Any], version
264264 3. Validate recovery action is triggered
265265 """
266266 daemon = launch_manager_daemon ["daemon" ]
267+ app_name = "rust_supervised_app" if version == "rust" else "cpp_supervised_app"
267268
268- # Give daemon enough time to perform supervision cycles and emit diagnostics.
269- time .sleep (2.0 )
270- logs = daemon .get_logs ()
271-
272- watchdog_like_signals = [
273- "Got kRunning timeout for process" ,
274- "Problem discovered in PG MainPG Activating Recovery state." ,
275- "unexpected termination of process" ,
276- ]
277- assert any (signal in logs for signal in watchdog_like_signals ), (
278- "No supervision/watchdog-related diagnostics found in daemon logs."
269+ # Setup: stop the supervised process to emulate a non-reporting workload.
270+ result = subprocess .run (
271+ ["pgrep" , "-f" , app_name ],
272+ capture_output = True ,
273+ text = True ,
274+ check = False ,
279275 )
276+ if result .returncode != 0 :
277+ # Some CI environments cannot keep startup apps alive due to uid/gid
278+ # switching restrictions. Do not skip: require target-specific
279+ # watchdog/recovery evidence from daemon logs instead.
280+ logs = daemon .get_logs ()
281+ watchdog_patterns = [
282+ rf"Got kRunning timeout for process.*\(\s*{ re .escape (app_name )} \s*\)" ,
283+ rf"unexpected termination of process.*\(\s*{ re .escape (app_name )} \s*\)" ,
284+ ]
285+ assert any (re .search (pattern , logs ) for pattern in watchdog_patterns ), (
286+ f"Target app { app_name } is not running and no target-specific watchdog diagnostics were found."
287+ f"\n Daemon logs:\n { logs } "
288+ )
289+ return
290+
291+ pid = result .stdout .strip ().split ("\n " )[0 ]
292+ subprocess .run (["kill" , "-STOP" , pid ], check = True )
293+ try :
294+ # Allow supervision/watchdog loop to detect stalled process.
295+ time .sleep (4.0 )
296+ logs = daemon .get_logs ()
297+ watchdog_patterns = [
298+ rf"Got kRunning timeout for process.*\(\s*{ re .escape (app_name )} \s*\)" ,
299+ rf"unexpected termination of process.*\(\s*{ re .escape (app_name )} \s*\)" ,
300+ ]
301+ assert any (re .search (pattern , logs ) for pattern in watchdog_patterns ), (
302+ f"No target-specific watchdog diagnostics found for { app_name } .\n Daemon logs:\n { logs } "
303+ )
304+ finally :
305+ subprocess .run (["kill" , "-CONT" , pid ], check = False )
0 commit comments