mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-06 20:43:46 +08:00
77 lines
2.9 KiB
Python
77 lines
2.9 KiB
Python
import signal
|
|
from unittest.mock import Mock, call, patch
|
|
|
|
import pytest
|
|
|
|
from openpilot.starpilot.models.recovery_process import ModeldProcess
|
|
from openpilot.system.manager import process
|
|
|
|
|
|
def owner(kind):
|
|
if kind == 'native':
|
|
return process.NativeProcess('worker', '.', ['unused'], lambda *_: True)
|
|
if kind == 'modeld':
|
|
result = ModeldProcess('modeld', 'unused', lambda *_: True)
|
|
result.context_ready = True
|
|
return result
|
|
return process.PythonProcess('starportd', 'unused', lambda *_: True)
|
|
|
|
|
|
@pytest.mark.parametrize('kind', ['python', 'native', 'modeld'])
|
|
@pytest.mark.parametrize('restart', [False, True])
|
|
def test_polling_stop_and_restart_never_wait_for_a_live_child(kind, restart):
|
|
p = owner(kind)
|
|
child = Mock(pid=123, exitcode=None)
|
|
p.proc = child
|
|
now = [100.]
|
|
with patch.object(process.time, 'monotonic', side_effect=lambda: now[0]), \
|
|
patch.object(process.os, 'kill') as kill, \
|
|
patch.object(process, 'join_process', side_effect=AssertionError('NONBLOCKING_STOP_WAITED')), \
|
|
patch.object(process, 'Process', side_effect=AssertionError('OVERLAPPING_CHILD')):
|
|
p.stop(block=False)
|
|
for elapsed in (0.5, 1., 4.99):
|
|
now[0] = 100. + elapsed
|
|
if restart:
|
|
p.start()
|
|
else:
|
|
p.stop(block=False)
|
|
assert p.proc is child and p.shutting_down
|
|
assert p.stop_requested_at == 100.
|
|
assert kill.call_args_list == [call(123, signal.SIGINT)]
|
|
now[0] = 105.
|
|
p.stop(block=False)
|
|
assert kill.call_args_list == [call(123, signal.SIGINT), call(123, signal.SIGKILL)]
|
|
child.join.assert_not_called()
|
|
child.exitcode = -signal.SIGKILL
|
|
assert p.stop(block=False) == -signal.SIGKILL
|
|
assert p.proc is None and not p.shutting_down and p.stop_requested_at is None
|
|
|
|
|
|
def test_nonblocking_stop_retry_false_preserves_original_escalation_deadline():
|
|
p = owner('python')
|
|
p.proc = Mock(pid=123, exitcode=None)
|
|
now = [100.]
|
|
with patch.object(process.time, 'monotonic', side_effect=lambda: now[0]), \
|
|
patch.object(process.os, 'kill') as kill, \
|
|
patch.object(process, 'join_process', side_effect=AssertionError('NONBLOCKING_STOP_WAITED')):
|
|
p.stop(retry=False, block=False)
|
|
now[0] = 106.
|
|
p.stop(retry=False, block=False)
|
|
assert kill.call_count == 1
|
|
p.stop(block=False)
|
|
assert kill.call_args.args == (123, signal.SIGKILL)
|
|
|
|
|
|
@pytest.mark.parametrize('retry', [False, True])
|
|
def test_explicit_blocking_shutdown_keeps_wait_and_kill_behavior(retry):
|
|
p = owner('python')
|
|
child = Mock(pid=123, exitcode=None)
|
|
p.proc = child
|
|
child.join.side_effect = lambda: setattr(child, 'exitcode', -signal.SIGKILL)
|
|
with patch.object(process.os, 'kill') as kill, patch.object(process, 'join_process') as wait:
|
|
result = p.stop(retry=retry)
|
|
wait.assert_called_once_with(child, 5)
|
|
assert kill.call_args.args == (123, signal.SIGKILL if retry else signal.SIGINT)
|
|
assert child.join.call_count == int(retry)
|
|
assert result == (-signal.SIGKILL if retry else None)
|