async def test_stop_while_paused(self) -> None: async with salobj.TestScript(index=self.index) as script: wait_time = 5 await self.configure_and_check(script, wait_time=wait_time) # set a stop checkpoint setCheckpoints_data = script.cmd_setCheckpoints.DataType() start_checkpoint = "start" setCheckpoints_data.pause = start_checkpoint await script.do_setCheckpoints(setCheckpoints_data) assert script.checkpoints.pause == start_checkpoint assert script.checkpoints.stop == "" run_data = script.cmd_run.DataType() asyncio.create_task(script.do_run(run_data)) while script.state.lastCheckpoint != "start": await asyncio.sleep(0) assert script.state.state == ScriptState.PAUSED stop_data = script.cmd_stop.DataType() await script.do_stop(stop_data) await asyncio.wait_for(script.done_task, timeout=STD_TIMEOUT) assert script.state.lastCheckpoint == start_checkpoint assert script.state.numCheckpoints == 1 assert script.state.state == ScriptState.STOPPED duration = (script.timestamps[ScriptState.STOPPING] - script.timestamps[ScriptState.RUNNING]) # the script ran quickly because we stopped the script # just as soon as it paused at the "start" checkpoint desired_duration = 0 print(f"test_stop_while_paused duration={duration:0.2f}") assert duration > 0.0 assert abs(duration - desired_duration) < 0.2
async def test_stop_while_running(self) -> None: async with salobj.TestScript(index=self.index) as script: wait_time = 5 pause_time = 0.5 await self.configure_and_check(script, wait_time=wait_time) start_checkpoint = "start" run_data = script.cmd_run.DataType() asyncio.create_task(script.do_run(run_data)) while script.state.lastCheckpoint != start_checkpoint: await asyncio.sleep(0) assert script.state.state == ScriptState.RUNNING await asyncio.sleep(pause_time) stop_data = script.cmd_stop.DataType() await script.do_stop(stop_data) await asyncio.wait_for(script.done_task, timeout=STD_TIMEOUT) assert script.state.lastCheckpoint == start_checkpoint assert script.state.numCheckpoints == 1 assert script.state.state == ScriptState.STOPPED duration = (script.timestamps[ScriptState.STOPPING] - script.timestamps[ScriptState.RUNNING]) # we waited `pause_time` seconds after the "start" checkpoint desired_duration = pause_time print(f"test_stop_while_running duration={duration:0.2f}") assert abs(duration - desired_duration) < 0.2
async def test_stop_at_checkpoint(self) -> None: async with salobj.TestScript(index=self.index) as script: wait_time = 0.1 await self.configure_and_check(script, wait_time=wait_time) # set a stop checkpoint setCheckpoints_data = script.cmd_setCheckpoints.DataType() end_checkpoint = "end" setCheckpoints_data.stop = end_checkpoint await script.do_setCheckpoints(setCheckpoints_data) assert script.checkpoints.pause == "" assert script.checkpoints.stop == end_checkpoint run_data = script.cmd_run.DataType() await asyncio.wait_for(script.do_run(run_data), timeout=STD_TIMEOUT) await asyncio.wait_for(script.done_task, timeout=STD_TIMEOUT) assert script.state.lastCheckpoint == end_checkpoint assert script.state.numCheckpoints == 2 assert script.state.state == ScriptState.STOPPED duration = (script.timestamps[ScriptState.STOPPING] - script.timestamps[ScriptState.RUNNING]) # waited and then stopped at the "end" checkpoint desired_duration = wait_time print(f"test_stop_at_checkpoint duration={duration:0.2f}") assert abs(duration - desired_duration) < 0.2
async def test_next_supplemented_group_id(self) -> None: async with salobj.TestScript(index=self.index) as script: await self.configure_and_check(script) group_id = script.group_id for i in range(5): # Format some other way than an f-string, # in order to have a different implementation than Script desired_supplemented_id = "%s#%s" % (group_id, i + 1) supplemented_id = script.next_supplemented_group_id() assert supplemented_id == desired_supplemented_id # Set a new group ID. This should reset the subgroup counter. new_group_id = group_id + " modified" group_id_data = script.cmd_setGroupId.DataType() group_id_data.groupId = new_group_id await script.do_setGroupId(group_id_data) for i in range(5): desired_supplemented_id = "%s#%s" % (new_group_id, i + 1) supplemented_id = script.next_supplemented_group_id() assert supplemented_id == desired_supplemented_id # Clear the group ID; getting a supplemened group ID should fail group_id_data = script.cmd_setGroupId.DataType() group_id_data.groupId = "" await script.do_setGroupId(group_id_data) with pytest.raises(RuntimeError): script.next_supplemented_group_id()
async def test_setCheckpoints(self) -> None: async with salobj.TestScript(index=self.index) as script: # try valid values data = script.cmd_setCheckpoints.DataType() for pause, stop in ( ("something", ""), ("", "something_else"), (".*", "start|end"), ): data.pause = pause data.stop = stop await script.do_setCheckpoints(data) assert script.checkpoints.pause == pause assert script.checkpoints.stop == stop # try with at least one checkpoint not a valid regex; # do_setCheckpoints should raise and not change the checkpoints initial_pause = "initial_pause" initial_stop = "initial_stop" data.pause = initial_pause data.stop = initial_stop await script.do_setCheckpoints(data) for bad_pause, bad_stop in (("(", ""), ("", "("), ("[", "[")): data.pause = bad_pause data.stop = bad_stop with pytest.raises(salobj.ExpectedError): await script.do_setCheckpoints(data) assert script.checkpoints.pause == initial_pause assert script.checkpoints.stop == initial_stop
async def test_set_state_and_attributes(self) -> None: async with salobj.TestScript(index=self.index) as script: # check keep_old_reason argument of set_state reason = "initial reason" additional_reason = "check append" await script.set_state(reason=reason) await script.set_state(reason=additional_reason, keep_old_reason=True) assert script.state.reason == reason + "; " + additional_reason bad_state = 1 + max(s.value for s in ScriptState) with pytest.raises(ValueError): await script.set_state(bad_state) script.state.state = bad_state assert script.state_name == f"UNKNOWN({bad_state})" assert not script._is_exiting await script.set_state(ScriptState.CONFIGURED) assert script.state_name == "CONFIGURED" # check assert_states all_states = set(ScriptState) for state in ScriptState: await script.set_state(state) assert script.state_name == state.name with pytest.raises(salobj.ExpectedError): script.assert_state( "should fail because state not in allowed states", all_states - set([state]), ) script.assert_state("should pass", [state]) script._is_exiting = True with pytest.raises(salobj.ExpectedError): script.assert_state("should fail because exiting", [state]) script._is_exiting = False # check that checkpoint is prohibited # unless state is RUNNING if state == ScriptState.RUNNING: continue with pytest.raises(RuntimeError): await script.checkpoint("foo") assert not script.done_task.done()
async def check_fail(self, fail_run: bool) -> None: """Check failure in run or cleanup. Parameters ---------- fail_run : `bool` If true then fail in the script's ``run`` method, else fail in the script's ``cleanup`` method. """ wait_time = 0.1 async with salobj.TestScript(index=self.index) as script: if fail_run: await self.configure_and_check(script, fail_run=True) else: await self.configure_and_check(script, fail_cleanup=True) run_data = script.cmd_run.DataType() await asyncio.wait_for(script.do_run(run_data), timeout=STD_TIMEOUT) if fail_run: await asyncio.wait_for(script.done_task, timeout=STD_TIMEOUT) assert script.state.lastCheckpoint == "start" assert script.state.numCheckpoints == 1 assert script.state.state == ScriptState.FAILED end_run_state = ScriptState.FAILING else: with pytest.raises(salobj.ExpectedError): await asyncio.wait_for(script.done_task, timeout=STD_TIMEOUT) assert script.state.lastCheckpoint == "end" assert script.state.numCheckpoints == 2 end_run_state = ScriptState.ENDING duration = (script.timestamps[end_run_state] - script.timestamps[ScriptState.RUNNING]) # if fail_run then failed before waiting, # otherwise failed after desired_duration = 0 if fail_run else wait_time print( f"test_fail duration={duration:0.3f} with fail_run={fail_run}") assert abs(duration - desired_duration) < 0.2
async def test_zero_index(self) -> None: with pytest.raises(ValueError): salobj.TestScript(index=0)
async def test_pause(self) -> None: async with salobj.TestScript(index=self.index) as script: # Cannot run in UNCONFIGURED state. run_data = script.cmd_run.DataType() with pytest.raises(salobj.ExpectedError): await script.do_run(run_data) # Test configure with data for a non-existent argument. configure_data = script.cmd_configure.DataType() configure_data.config = "no_such_arg: 1" with pytest.raises(salobj.ExpectedError): await script.do_configure(configure_data) assert script.state.state == ScriptState.UNCONFIGURED # Test configure with invalid yaml. configure_data = script.cmd_configure.DataType() configure_data.config = "a : : 2" with pytest.raises(salobj.ExpectedError): await script.do_configure(configure_data) assert script.state.state == ScriptState.UNCONFIGURED # Test configure with yaml that makes a string, not a dict. configure_data = script.cmd_configure.DataType() configure_data.config = "just_a_string" with pytest.raises(salobj.ExpectedError): await script.do_configure(configure_data) assert script.state.state == ScriptState.UNCONFIGURED # Test configure with yaml that makes a list, not a dict. configure_data = script.cmd_configure.DataType() configure_data.config = "['not', 'a', 'dict']" with pytest.raises(salobj.ExpectedError): await script.do_configure(configure_data) assert script.state.state == ScriptState.UNCONFIGURED # Now test valid configuration; specify nonexistent checkpoints # to test that the configure command handles checkpoints at all. wait_time = 0.5 # Specify a log level that is not the default (which is INFO) # and is only slightly more verbose than INFO. log_level = logging.INFO - 1 prelim_pause_checkpoint = "preliminary nonexistent pause checkpoint" prelim_stop_checkpoint = "preliminary nonexistent stop checkpoint" await self.configure_and_check( script, wait_time=wait_time, log_level=log_level, pause_checkpoint=prelim_pause_checkpoint, stop_checkpoint=prelim_stop_checkpoint, ) assert script.state.numCheckpoints == 0 # Set a pause checkpoint that exists. setCheckpoints_data = script.cmd_setCheckpoints.DataType() start_checkpoint = "start" end_checkpoint = "end" nonexistent_checkpoint = "nonexistent checkpoint" setCheckpoints_data.pause = start_checkpoint setCheckpoints_data.stop = nonexistent_checkpoint await script.do_setCheckpoints(setCheckpoints_data) assert script.checkpoints.pause == start_checkpoint assert script.checkpoints.stop == nonexistent_checkpoint # Run the script. run_data = script.cmd_run.DataType() run_task = asyncio.create_task(script.do_run(run_data)) niter = 0 while script.state.state != ScriptState.PAUSED: niter += 1 await asyncio.sleep(0) assert script.state.lastCheckpoint == start_checkpoint assert script.state.numCheckpoints == 1 assert script.checkpoints.pause == start_checkpoint assert script.checkpoints.stop == nonexistent_checkpoint resume_data = script.cmd_resume.DataType() await script.do_resume(resume_data) await asyncio.wait_for(run_task, timeout=STD_TIMEOUT) await asyncio.wait_for(script.done_task, timeout=STD_TIMEOUT) assert script.state.lastCheckpoint == end_checkpoint assert script.state.numCheckpoints == 2 duration = (script.timestamps[ScriptState.ENDING] - script.timestamps[ScriptState.RUNNING]) desired_duration = wait_time print(f"test_pause duration={duration:0.2f}") assert abs(duration - desired_duration) < 0.2