@@ -663,27 +663,44 @@ async def passthrough_awaitable(awaitable):
663663 @pytest .mark .asyncio
664664 async def test_stay_connected_menu_interruptions (self ):
665665 """Test the stay_connected_menu being interrupted by a power button press or hub disconnect."""
666- disconnect_call_count = 0
667- power_call_count = 0
666+ mock_menu_call_count = 0
668667
669668 # simulates the hub disconnecting on the first call,
670669 async def mock_race_disconnect (awaitable ):
671- task = asyncio .ensure_future (awaitable )
672- nonlocal disconnect_call_count
673- disconnect_call_count += 1
674- if disconnect_call_count == 1 :
670+ task = asyncio .create_task (awaitable )
671+ try :
672+ if mock_menu_call_count == 1 :
673+ await asyncio .sleep (0.01 )
674+ task .cancel ()
675+ raise HubDisconnectError ("hub disconnected" )
676+ return await task
677+ except BaseException :
678+ await asyncio .sleep (0.01 )
675679 task .cancel ()
676- raise HubDisconnectError ("hub disconnected" )
677- return await awaitable
680+ raise
678681
682+ # simulate the power button being pressed on the second call
679683 async def mock_race_power_button_press (awaitable ):
680- task = asyncio .ensure_future (awaitable )
681- nonlocal power_call_count
682- power_call_count += 1
683- if power_call_count == 2 :
684+ task = asyncio .create_task (awaitable )
685+ try :
686+ if mock_menu_call_count == 2 :
687+ await asyncio .sleep (0.01 )
688+ task .cancel ()
689+ raise HubPowerButtonPressedError ("power button pressed" )
690+ return await task
691+ except BaseException :
692+ await asyncio .sleep (0.01 )
684693 task .cancel ()
685- raise HubPowerButtonPressedError ("power button pressed" )
686- return await awaitable
694+ raise
695+
696+ # should be called but cancelled twice, returning "Recompile and Run" the third time
697+ async def mock_menu_function ():
698+ nonlocal mock_menu_call_count
699+ mock_menu_call_count += 1
700+ if mock_menu_call_count <= 3 :
701+ return "Recompile and Run"
702+ else :
703+ return "Exit"
687704
688705 # Create a mock hub
689706 mock_hub = AsyncMock ()
@@ -700,12 +717,11 @@ async def mock_race_power_button_press(awaitable):
700717 mock_hub ._wait_for_user_program_stop = AsyncMock ()
701718 # create a mock questionary menu
702719 mock_menu = AsyncMock ()
703- mock_menu .ask_async .side_effect = [
704- "Recompile and Run" ,
705- "Exit" ,
706- ]
707- mock_confirm = AsyncMock ()
708- mock_confirm .ask_async .return_value = True
720+ mock_menu .ask_async .side_effect = mock_menu_function
721+
722+ # create a mock confirmation menu to reconnect to the hub
723+ mock_confirm_menu = AsyncMock ()
724+ mock_confirm_menu .ask_async .return_value = True
709725
710726 # Set up mocks using ExitStack
711727 with contextlib .ExitStack () as stack :
@@ -742,17 +758,25 @@ async def mock_race_power_button_press(awaitable):
742758 mock_selector = stack .enter_context (
743759 patch ("questionary.select" , return_value = mock_menu )
744760 )
745- stack .enter_context (patch ("questionary.confirm" , return_value = mock_confirm ))
761+ mock_confirm = stack .enter_context (
762+ patch ("questionary.confirm" , return_value = mock_confirm_menu )
763+ )
746764
747765 # Run the command
748766 run_cmd = Run ()
749767 await run_cmd .stay_connected_menu (mock_hub , args )
750768
751769 assert mock_selector .call_count == 4
770+ # a confirmation menu should be triggered and the hub should be re-instantiated upon a HubDisconnectError
771+ mock_confirm .assert_called_once ()
752772 mock_hub_class .assert_called_once ()
753773 mock_hub .connect .assert_called_once ()
774+
775+ # these functions should be triggered upon a HubPowerButtonPressedError
754776 mock_hub ._wait_for_power_button_release .assert_called_once ()
755777 mock_hub ._wait_for_user_program_stop .assert_called_once ()
778+
779+ # this should only be called once because the menu was canceled the first two times it was called
756780 mock_hub .run .assert_called_once_with (temp_path , wait = True )
757781
758782
0 commit comments