@@ -625,6 +625,80 @@ async def test_command_process_terminate_timeout_kills() -> None:
625625 assert process .returncode == - 9
626626
627627
628+ @pytest .mark .asyncio
629+ async def test_command_process_terminate_returns_when_already_exited () -> None :
630+ process = _FakeCommandProcess (returncode = 0 )
631+
632+ await command_module ._terminate_process (cast (Any , process ), timeout = 0.001 )
633+
634+ assert process .terminate_calls == 0
635+ assert process .kill_calls == 0
636+
637+
638+ @pytest .mark .asyncio
639+ async def test_command_process_terminate_returns_when_timeout_sets_returncode () -> None :
640+ @dataclass (slots = True )
641+ class _ExitingOnCancelledProcess (_FakeCommandProcess ):
642+ async def wait (self ) -> int :
643+ try :
644+ await asyncio .sleep (1 )
645+ except asyncio .CancelledError :
646+ self .returncode = 143
647+ raise
648+ return self .returncode or 0 # pragma: no cover
649+
650+ process = _ExitingOnCancelledProcess ()
651+
652+ await command_module ._terminate_process (cast (Any , process ), timeout = 0.001 )
653+
654+ assert process .terminate_calls == 1
655+ assert process .kill_calls == 0
656+ assert process .returncode == 143
657+
658+
659+ @pytest .mark .asyncio
660+ async def test_command_process_terminate_kills_when_cancelled () -> None :
661+ process = _FakeCommandProcess (wait_delay = 0.01 )
662+ task = asyncio .create_task (
663+ command_module ._terminate_process (cast (Any , process ), timeout = 1 ),
664+ )
665+
666+ await asyncio .sleep (0 )
667+ task .cancel ()
668+ with pytest .raises (asyncio .CancelledError ):
669+ await task
670+
671+ assert process .terminate_calls == 1
672+ assert process .kill_calls == 1
673+
674+
675+ @pytest .mark .asyncio
676+ async def test_command_process_terminate_cancelled_after_process_exit () -> None :
677+ @dataclass (slots = True )
678+ class _ExitedOnCancelledProcess (_FakeCommandProcess ):
679+ async def wait (self ) -> int :
680+ try :
681+ await asyncio .sleep (1 )
682+ except asyncio .CancelledError :
683+ self .returncode = 0
684+ raise
685+ return self .returncode or 0 # pragma: no cover
686+
687+ process = _ExitedOnCancelledProcess ()
688+ task = asyncio .create_task (
689+ command_module ._terminate_process (cast (Any , process ), timeout = 1 ),
690+ )
691+
692+ await asyncio .sleep (0 )
693+ task .cancel ()
694+ with pytest .raises (asyncio .CancelledError ):
695+ await task
696+
697+ assert process .terminate_calls == 1
698+ assert process .kill_calls == 0
699+ assert process .returncode == 0
700+
701+
628702@pytest .mark .asyncio
629703async def test_command_connection_covers_all_done_path (
630704 monkeypatch : pytest .MonkeyPatch ,
0 commit comments