Files

62 lines
3.5 KiB
Python

import sys
import tempfile
import threading
import time
from unittest import TestCase
from station.processes import ProcessSupervisor
from station.tasks import Cancelled
class ProcessesTest(TestCase):
def test_owned_process_cancel_and_exit(self):
with tempfile.TemporaryDirectory() as directory:
processes=ProcessSupervisor(directory)
try:
result=processes.run('echo',[sys.executable,'-c','print("completed")'],threading.Event())
self.assertTrue(result['ok']);self.assertIn('completed',result['detail'])
event=threading.Event();timer=threading.Timer(.2,event.set);timer.start()
with self.assertRaises(Cancelled):
processes.run('long',[sys.executable,'-c','import time;time.sleep(30)'],event)
self.assertFalse(processes.snapshot()['long']['running'])
finally: processes.close()
def test_each_run_has_its_own_log_slice_and_explicit_owner(self):
with tempfile.TemporaryDirectory() as directory:
processes=ProcessSupervisor(directory)
try:
processes.run('tool:89',[sys.executable,'-c','print("OLD_FAILURE")'],threading.Event())
result=processes.run('tool:89',[sys.executable,'-c','print("NEW_RESULT")'],threading.Event())
self.assertIn('NEW_RESULT',result['detail']);self.assertNotIn('OLD_FAILURE',result['detail'])
row=processes.snapshot()['tool:89']
self.assertEqual(row['owner'],'station');self.assertEqual(row['state'],'exited')
finally: processes.close()
def test_success_waits_for_orphaned_child_cleanup(self):
from pathlib import Path
from arm_application.process_lifecycle import _stat
with tempfile.TemporaryDirectory() as directory:
processes=ProcessSupervisor(directory)
pidfile=Path(directory)/'pid'
source="import subprocess,sys,time;from pathlib import Path;p=subprocess.Popen([sys.executable,'-c','import time;time.sleep(120)'],start_new_session=True);Path(sys.argv[1]).write_text(str(p.pid))"
try:
result=processes.run('motion:89',[sys.executable,'-c',source,str(pidfile)],threading.Event(),timeout=12)
self.assertTrue(result['ok'],result)
child=int(pidfile.read_text());state=_stat(child)
self.assertTrue(state is None or state[2]=='Z','task reported success with a live orphan')
finally: processes.close()
def test_process_cleanup_phase_is_reported_before_resource_release(self):
from pathlib import Path
with tempfile.TemporaryDirectory() as directory:
processes=ProcessSupervisor(directory)
ready=Path(directory)/'ready'
child="import signal,time,sys;from pathlib import Path;signal.signal(signal.SIGINT,lambda *a:(time.sleep(.4),sys.exit(0)));Path(sys.argv[1]).write_text('ready');time.sleep(20)"
source="import subprocess,sys,time;from pathlib import Path;subprocess.Popen([sys.executable,'-c',sys.argv[1],sys.argv[2]],start_new_session=True)\nwhile not Path(sys.argv[2]).exists():time.sleep(.01)"
phases=[]
try:
result=processes.run('motion:89',[sys.executable,'-c',source,child,str(ready)],threading.Event(),timeout=8,on_phase=phases.append)
self.assertTrue(result['ok'],result)
self.assertIn('收尾',phases)
self.assertFalse(processes.snapshot()['motion:89']['running'])
finally: processes.close()