the-custodian/docs/changes/netkingdom-target-2026-10-17/native-runtime-install-r2/test_process_supervision.py

105 lines
5.5 KiB
Python

#!/usr/bin/env python3
"""Disposable actual process-tree oracles; never invoke runtime, SSH or bwrap."""
import ctypes
import importlib.util
import json
import os
from pathlib import Path
import select
import signal
import subprocess
import sys
import tempfile
import time
import unittest
HERE=Path(__file__).resolve().parent
SPEC=importlib.util.spec_from_file_location('supervised',HERE/'process_supervision.py')
S=importlib.util.module_from_spec(SPEC);SPEC.loader.exec_module(S)
CANARY='fixture-private-stderr-must-not-appear'
# A session-escaping grandchild remains owned by the subreaper even after the
# intermediate process exits. No namespace or application launcher is involved.
LEAF="import os,time,sys;from pathlib import Path;Path(sys.argv[1]).write_text(str(os.getpid()));print(sys.argv[2],file=sys.stderr,flush=True);time.sleep(30)"
LEADER="import subprocess,sys,time;subprocess.Popen([sys.executable,'-c',sys.argv[1],sys.argv[2],sys.argv[3]],start_new_session=True);time.sleep(30)"
def wait_until(predicate,timeout=8):
end=time.monotonic()+timeout
while time.monotonic()<end:
if predicate():return
time.sleep(.02)
raise AssertionError('bounded fixture condition failed')
def not_alive(pid):
f=S.facts(pid)
return f is None or f['state']=='Z'
class ProcessSupervisionTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
# Reap only the known detached fixture watchdog after killed-controller
# tests, so this test does not depend on the workstation PID1 reaper.
assert ctypes.CDLL(None).prctl(36,1,0,0,0)==0
def setUp(self):
self.temp=tempfile.TemporaryDirectory(prefix='nk-supervision-fixture-')
self.base=Path(self.temp.name);self.sentinel=subprocess.Popen([sys.executable,'-c','import time;time.sleep(30)'])
self.addCleanup(self.cleanup)
def cleanup(self):
self.sentinel.terminate();self.sentinel.wait(timeout=3);self.temp.cleanup()
def argv(self,marker):return [sys.executable,'-c',LEADER,LEAF,str(marker),CANARY]
def assert_quiet(self,state,pid):
wait_until(lambda:(state/'quiescent.json').exists())
r=json.loads((state/'quiescent.json').read_text())
self.assertTrue(r['quiescent']);self.assertTrue(r['no_live_or_waitable_descendants'])
wait_until(lambda:not_alive(pid));self.assertIsNone(self.sentinel.poll())
for p in state.glob('*.json'):
if p.name.startswith('request-'):continue # test input deliberately contains canary
self.assertNotIn(CANARY,p.read_text())
return r
def test_success_and_bounded_stdout(self):
s=S.Supervisor(self.base/'state')
self.assertEqual(s.run([sys.executable,'-c','print("receipt")'],2,capture=True),(0,b'receipt\n'))
self.assertTrue(s.close()['quiescent'])
def test_timeout_reaps_session_escaped_descendant_and_retains_sibling(self):
state=self.base/'state';marker=self.base/'child';s=S.Supervisor(state)
code,output=s.run(self.argv(marker),.6,capture=True)
self.assertEqual(code,124);self.assertEqual(output,b'')
self.assertTrue(marker.exists());r=self.assert_quiet(state,int(marker.read_text()))
self.assertEqual(r['reason'],'timeout');self.assertTrue(s.close()['quiescent'])
def test_hard_controller_loss_reaps_descendant_and_retains_stage(self):
state=self.base/'state';marker=self.base/'child'
controller="import importlib.util,sys;from pathlib import Path;s=importlib.util.spec_from_file_location('s',sys.argv[1]);m=importlib.util.module_from_spec(s);s.loader.exec_module(m);x=m.Supervisor(Path(sys.argv[2]));x.run(sys.argv[3:],20,capture=True)"
p=subprocess.Popen([sys.executable,'-I','-B','-c',controller,str(HERE/'process_supervision.py'),str(state),*self.argv(marker)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
self.addCleanup(lambda:p.poll() is None and p.kill())
wait_until(marker.exists);watchdog=json.loads((state/'ready.json').read_text())['pid']
os.kill(p.pid,signal.SIGKILL);p.wait(timeout=3)
r=self.assert_quiet(state,int(marker.read_text()));self.assertEqual(r['reason'],'controller_lost')
self.assertTrue(state.exists()) # no controller remains to delete its stage
wait_until(lambda:not_alive(watchdog));os.waitpid(watchdog,0)
def test_outer_phase_timeout_stops_nested_runner_sibling(self):
state=self.base/'state';marker=self.base/'child';s=S.Supervisor(state)
phase="import importlib.util,sys;from pathlib import Path;s=importlib.util.spec_from_file_location('s',sys.argv[1]);m=importlib.util.module_from_spec(s);s.loader.exec_module(m);m.request(Path(sys.argv[2]),sys.argv[3:],20)"
code,output=s.run([sys.executable,'-I','-B','-c',phase,str(HERE/'process_supervision.py'),str(state),*self.argv(marker)],.7,capture=True)
self.assertEqual(code,124);self.assertTrue(marker.exists())
self.assert_quiet(state,int(marker.read_text()));self.assertTrue(s.close()['quiescent'])
def test_invalid_mapping_retains_failed_quiescence_evidence(self):
state=self.base/'state';s=S.Supervisor(state);owner=S.facts(os.getpid());owner['start']+=1
S.write(state/'request-invalid.json',{'owner':owner,'argv':[sys.executable,'-c','pass'],'timeout':1,'capture':False})
wait_until(lambda:(state/'quiescent.json').exists())
self.assertFalse(json.loads((state/'quiescent.json').read_text())['quiescent'])
with self.assertRaises(RuntimeError):s.close()
self.assertTrue(state.exists());self.assertIsNone(self.sentinel.poll())
if __name__=='__main__':unittest.main()