Ejemplo n.º 1
0
def test_scan_id_persists_between_executions(
    script_that_increments_and_returns_scan_id,
):
    """
    The scan ID should be shared and persisted between process executions.
    """
    manager = ProcessManager()
    queue = multiprocessing.Queue()
    run_args = ProcedureInput(queue)

    pid = manager.create(
        script_uri=script_that_increments_and_returns_scan_id,
        init_args=ProcedureInput(),
    )
    manager.run(pid, run_args=run_args)
    wait_for_process_to_complete(manager)
    scan_id = queue.get(timeout=1)

    pid = manager.create(
        script_uri=script_that_increments_and_returns_scan_id,
        init_args=ProcedureInput(),
    )
    manager.run(pid, run_args=run_args)
    wait_for_process_to_complete(manager)
    next_scan_id = queue.get(timeout=1)

    assert next_scan_id == scan_id + 1
Ejemplo n.º 2
0
def test_ses_prepare_call_sequence_and_returns_summary_for_created_process():
    """
    Verify that ScriptExecutionService.prepare() calls the appropriate domain
    object methods for process creation and returns the expected summary object
    """
    script_uri = "test://test.py"
    cmd = PrepareProcessCommand(script_uri=script_uri, init_args=ProcedureInput())
    procedure = Procedure(script_uri, procedure_id=123)
    procedures = {123: procedure}
    expected = ProcedureSummary(
        id=123,
        script_uri=procedure.script_uri,
        script_args=procedure.script_args,
        history=procedure.history,
        state=procedure.state,
    )

    with mock.patch(
        "oet.procedure.application.application.domain.ProcessManager"
    ) as mock_pm:
        # get the mock ProcessManager instance
        instance = mock_pm.return_value
        # tell ProcessManager.create to return PID 123, which is subsequently
        # used for lookup
        instance.create.return_value = 123
        # the manager's procedures attribute holds created procedures and is
        # used for retrieval
        instance.procedures = procedures

        service = ScriptExecutionService()
        returned = service.prepare(cmd)

        instance.create.assert_called_once_with(script_uri, init_args=ProcedureInput())
        assert returned == expected
Ejemplo n.º 3
0
def test_process_manager_updates_state_of_completed_procedures(manager, script_path):
    """
    Verify that ProcessManager updates procedure state to COMPLETED when finished
    successfully
    """
    pid = manager.create(script_path, init_args=ProcedureInput())
    manager.run(pid, run_args=ProcedureInput())
    wait_for_process_to_complete(manager)
    assert manager.procedures[pid].state == ProcedureState.COMPLETED
Ejemplo n.º 4
0
def test_process_manager_sets_running_to_none_on_script_failure(manager, fail_script):
    """
    Verify that ProcessManager sets running procedure attribute to None
    when script execution fails
    """
    pid = manager.create(fail_script, init_args=ProcedureInput())
    manager.run(pid, run_args=ProcedureInput())
    wait_for_process_to_complete(manager)
    assert manager.running is None
Ejemplo n.º 5
0
def test_process_manager_updates_procedure_state_on_stop(manager, abort_script):
    """
    Verify that ProcessManager removes an stopped procedure from
    the procedures list
    """
    pid = manager.create(abort_script, init_args=ProcedureInput())
    manager.run(pid, run_args=ProcedureInput())
    manager.stop(pid)
    wait_for_process_to_complete(manager)
    assert manager.procedures[pid].state == ProcedureState.STOPPED
Ejemplo n.º 6
0
def test_procedure_input_eq_works_as_expected():
    """
    Verify ProcedureInput equality
    """
    pi1 = ProcedureInput(1, 2, 3, a=1, b=2)
    pi2 = ProcedureInput(1, 2, 3, a=1, b=2)
    pi3 = ProcedureInput(4, a=1)
    assert pi1 == pi2
    assert pi1 != pi3
    assert pi1 != object()
Ejemplo n.º 7
0
def test_process_manager_sets_running_to_none_on_stop(manager, abort_script):
    """
    Verify that ProcessManager sets running procedure attribute to None
    when script is stopped
    """
    pid = manager.create(abort_script, init_args=ProcedureInput())
    manager.run(pid, run_args=ProcedureInput())
    manager.stop(pid)
    wait_for_process_to_complete(manager)
    assert manager.running is None
Ejemplo n.º 8
0
def test_process_manager_run_changes_state_of_procedure_to_running(
    manager, script_path, process_cleanup
):
    """
    Verify that procedure state changes when ProcessManager starts
    procedure execution
    """
    pid = manager.create(script_path, init_args=ProcedureInput())
    assert manager.procedures[pid].state == ProcedureState.CREATED
    manager.run(pid, run_args=ProcedureInput())
    assert manager.procedures[pid].state == ProcedureState.RUNNING
Ejemplo n.º 9
0
def test_process_manager_run_fails_on_process_that_is_already_running(
    manager, script_path, process_cleanup
):
    """
    Verify that an exception is raised when requesting run() for a procedure
    that is already running
    """
    pid = manager.create(script_path, init_args=ProcedureInput())
    manager.run(pid, run_args=ProcedureInput())
    with pytest.raises(ValueError):
        manager.run(pid, run_args=ProcedureInput())
Ejemplo n.º 10
0
def test_process_manager_stop_terminates_the_process(manager, abort_script):
    """
    Verify that ProcessManager stops a script execution
    """
    pid = manager.create(abort_script, init_args=ProcedureInput())
    created = manager.procedures[pid]
    queue = multiprocessing.Queue()
    manager.run(pid, run_args=ProcedureInput(queue, created))
    manager.stop(pid)
    wait_for_process_to_complete(manager, timeout=3)
    assert queue.empty()
Ejemplo n.º 11
0
def test_process_manager_sets_running_to_none_when_process_completes(
    manager, script_path
):
    """
    Verify that ProcessManager sets running procedure attribute to None
    when process completes
    """
    pid = manager.create(script_path, init_args=ProcedureInput())
    manager.run(pid, run_args=ProcedureInput())
    wait_for_process_to_complete(manager)
    assert manager.running is None
Ejemplo n.º 12
0
def test_process_manager_updates_procedure_state_on_script_failure(
    manager, fail_script
):
    """
    Verify that ProcessManager removes a failed procedure from
    the procedures list
    """
    pid = manager.create(fail_script, init_args=ProcedureInput())
    manager.run(pid, run_args=ProcedureInput())
    wait_for_process_to_complete(manager)
    assert manager.procedures[pid].state == ProcedureState.FAILED
Ejemplo n.º 13
0
def test_calling_process_manager_run_sets_run_args_on_procedure(
    manager, script_path, process_cleanup
):
    """
    Verify that the arguments to ProcessManager run() are captured and stored on the
    procedure instance
    """
    pid = manager.create(script_path, init_args=ProcedureInput())
    expected = ProcedureInput(5, 6, 7, kw3="c", kw4="d")
    created = manager.procedures[pid]
    manager.run(pid, run_args=expected)
    assert created.script_args["run"] == expected
Ejemplo n.º 14
0
def test_process_manager_create_adds_new_procedure(manager, script_path):
    """
    Verify that ProcessManager keeps references to the processes it creates
    """
    len_before = len(manager.procedures)
    manager.create(script_path, init_args=ProcedureInput())
    assert len(manager.procedures) == len_before + 1
Ejemplo n.º 15
0
def test_process_manager_create_sets_pid_of_new_procedure(manager, script_path):
    """
    Verify that procedures are assigned IDs on process creation
    """
    pid = manager.create(script_path, init_args=ProcedureInput())
    created = manager.procedures[pid]
    assert created.id == pid
Ejemplo n.º 16
0
def test_process_manager_run_fails_on_invalid_pid(manager):
    """
    Verify that an exception is raised when run() is requested for an invalid
    PID
    """
    with pytest.raises(ValueError):
        manager.run(321, run_args=ProcedureInput())
Ejemplo n.º 17
0
def test_procedure_input_accepts_expected_constructor_values():
    """
    Verify that ProcedureInput arguments are slurped into positional and
    keyword/value attributes.
    """
    procedure_input = ProcedureInput(1, 2, 3, a=1, b=2)
    assert procedure_input.args == (1, 2, 3)
    assert procedure_input.kwargs == dict(a=1, b=2)
Ejemplo n.º 18
0
def test_process_manager_updates_history_of_completed_procedures(manager, script_path):
    """
    Verify that ProcessManager updates procedure state to COMPLETED when finished
    successfully
    """
    pid = manager.create(script_path, init_args=ProcedureInput())
    manager.run(pid, run_args=ProcedureInput())
    wait_for_process_to_complete(manager)
    procedure = manager.procedures[pid]

    assert ProcedureState.CREATED in procedure.history.process_states
    assert isinstance(procedure.history.process_states[ProcedureState.CREATED], float)
    assert ProcedureState.RUNNING in procedure.history.process_states
    assert isinstance(procedure.history.process_states[ProcedureState.RUNNING], float)
    assert ProcedureState.COMPLETED in procedure.history.process_states
    assert isinstance(procedure.history.process_states[ProcedureState.COMPLETED], float)
    assert procedure.history.stacktrace is None
Ejemplo n.º 19
0
def test_process_manager_updates_procedure_history_on_script_failure(
    manager, fail_script
):
    """
    Verify that ProcessManager updates FAILED to procedure history when script fails
    """
    pid = manager.create(fail_script, init_args=ProcedureInput())
    manager.run(pid, run_args=ProcedureInput())
    wait_for_process_to_complete(manager)
    procedure = manager.procedures[pid]

    assert ProcedureState.CREATED in procedure.history.process_states
    assert isinstance(procedure.history.process_states[ProcedureState.CREATED], float)
    assert ProcedureState.RUNNING in procedure.history.process_states
    assert isinstance(procedure.history.process_states[ProcedureState.RUNNING], float)
    assert ProcedureState.FAILED in procedure.history.process_states
    assert isinstance(procedure.history.process_states[ProcedureState.FAILED], float)
    assert procedure.history.stacktrace is not None
Ejemplo n.º 20
0
def test_runtime_arguments_are_passed_to_user_script(procedure):
    """
    Verify that arguments passed from procedure are accessible in the user script
    """
    run_args = ProcedureInput(5, 6, 7, kw3="c", kw4="d")
    procedure.script_args["run"] = run_args
    procedure.user_module = MagicMock()
    procedure.run()
    procedure.user_module.main.assert_called_with(5, 6, 7, kw3="c", kw4="d")
Ejemplo n.º 21
0
def test_process_manager_create_captures_initialisation_arguments(manager, script_path):
    """
    Verify that ProcessManager passes through initialisation arguments to
    the procedures it creates
    """
    expected = ProcedureInput(1, 2, 3, a=4, b=5)
    pid = manager.create(script_path, init_args=expected)
    created = manager.procedures[pid]
    assert created.script_args["init"] == expected
Ejemplo n.º 22
0
def test_process_manager_run_executes_procedure_start(manager, process_cleanup):
    """
    Verify that a call to ProcessManager run() executes Procedure.start()
    instead of Procedure.run(). This confirms that Procedure will execute in a
    child process.
    """
    procedure = MagicMock()
    manager.procedures[1] = procedure
    manager.run(1, run_args=ProcedureInput())
    procedure.start.assert_called_once()
Ejemplo n.º 23
0
def create_empty_procedure_summary(
    procedure_id: int, script_uri: str, history: ProcedureHistory
):
    """
    Utility function to create a null procedure summary. The returned
    procedure defines zero script arguments.

    :param procedure_id: procedure ID
    :param script_uri: path to script
    :param history: Procedure history
    :return: corresponding ProcedureSummary object
    """
    return ProcedureSummary(
        id=procedure_id,
        script_uri=script_uri,
        script_args={"init": ProcedureInput(), "run": ProcedureInput()},
        history=history,
        state=ProcedureState.CREATED,
    )
Ejemplo n.º 24
0
def test_process_manager_stop_fails_on_process_that_is_not_running(
    manager, script_path
):
    """
    Verify that an exception is raised when requesting stop() for a procedure
    that is not running
    """
    pid = manager.create(script_path, init_args=ProcedureInput())
    with pytest.raises(ValueError):
        manager.stop(pid)
Ejemplo n.º 25
0
def test_process_manager_run_sets_running_procedure(manager, tmpdir, process_cleanup):
    """
    Verify that ProcessManager sets the running procedure attribute
    appropriately when run() is called
    """
    script_path = tmpdir.join("sleep.py")
    script_path.write(
        """
def main(shutdown_event, *args, **kwargs):
    while not shutdown_event.is_set():
        continue
"""
    )
    script_uri = f"file://{str(script_path)}"

    shutdown_event = multiprocessing.Event()
    pid = manager.create(script_uri, init_args=ProcedureInput())
    manager.run(pid, run_args=ProcedureInput(shutdown_event))
    assert manager.running == manager.procedures[pid]
    shutdown_event.set()
Ejemplo n.º 26
0
def test_process_manager_create_removes_oldest_procedure_on_max_procedures(
    manager, script_path
):
    """
    Verify that ProcessManager removes the oldest procedure when the maximum number of
    saved procedures is reached
    """
    manager.procedures.clear()
    max_procedures = PROCEDURE_QUEUE_MAX_LENGTH
    for _ in range(len(manager.procedures), max_procedures):
        manager.create(script_path, init_args=ProcedureInput())

    assert len(manager.procedures) == max_procedures
    assert 1 in manager.procedures

    # adding procedure should not increase the number of procedures
    # and should remove the oldest procedure (with ID 1)
    manager.create(script_path, init_args=ProcedureInput())

    assert len(manager.procedures) == max_procedures
    assert 1 not in manager.procedures
Ejemplo n.º 27
0
def test_ses_start_calls_process_manager_function_and_returns_summary():
    """
    Verify that ScriptExecutionService.start() calls the appropriate domain
    object methods for starting process execution and returns the expected
    summary object
    """
    script_uri = "test://test.py"
    cmd = StartProcessCommand(process_uid=123, run_args=ProcedureInput())
    procedure = Procedure(script_uri, procedure_id=123)
    procedures = {123: procedure}
    expected = ProcedureSummary(
        id=123,
        script_uri=procedure.script_uri,
        script_args=procedure.script_args,
        history=procedure.history,
        state=procedure.state,
    )

    with mock.patch(
        "oet.procedure.application.application.domain.ProcessManager"
    ) as mock_pm:
        # get the mock ProcessManager instance
        instance = mock_pm.return_value
        # the manager's procedures attribute holds created procedures and is
        # used for retrieval
        instance.procedures = procedures

        service = ScriptExecutionService()
        returned = service.start(cmd)

        # service should call run() and return the summary for the executed
        # procedure
        instance.run.assert_called_once_with(123, run_args=ProcedureInput())
        assert returned == expected
        # we don't validate or modify procedure state, so this should still be
        # READY rather than RUNNING
        assert returned.state == ProcedureState.CREATED
Ejemplo n.º 28
0
def test_ses_stop_calls_process_manager_function_with_no_script_execution(abort_script):
    """
    Verify that ScriptExecutionService.stop() calls the appropriate domain
    object methods for stopping process execution without executing abort
    python script.
    """
    # PID of running process
    running_pid = 123

    # Test script/procedures will target sub-array 2
    init_args = ProcedureInput(subarray_id=2)

    # Create Procedure representing the script to be stopped
    procedure_to_stop = Procedure("test://a")
    procedure_to_stop.script_args["init"] = init_args

    # Prepare a dict of PIDs to Procedures that we can use to mock the internal
    # data structure held by ProcessManager.
    process_manager_procedures = {running_pid: procedure_to_stop}

    cmd = StopProcessCommand(process_uid=running_pid, run_abort=False)
    # returned summary list should be empty if abort script is bypassed
    expected = []

    with mock.patch(
        "oet.procedure.application.application.domain.ProcessManager"
    ) as mock_pm:
        # get the mock ProcessManager instance, preparing it for SES access
        instance = mock_pm.return_value
        instance.procedures = process_manager_procedures

        service = ScriptExecutionService(abort_script_uri=abort_script)
        returned = service.stop(cmd)

        # service should call stop() and return empty list
        instance.stop.assert_called_once_with(running_pid)
        assert returned == expected
Ejemplo n.º 29
0
def test_ses_get_subarray_id_for_requested_pid():
    """
    Verify that the private method _get_subarray_id returns
    subarray id correctly
    """
    subarray_id = 123
    process_pid = 456

    procedure = Procedure("test://a")
    init_args = ProcedureInput(subarray_id=subarray_id)
    procedure.script_args["init"] = init_args
    procedures = {process_pid: procedure}
    process_summary = ProcedureSummary(
        id=process_pid,
        script_uri=procedure.script_uri,
        script_args=procedure.script_args,
        history=procedure.history,
        state=procedure.state,
    )
    expected = [process_summary]

    with mock.patch(
        "oet.procedure.application.application.domain.ProcessManager"
    ) as mock_pm:
        # get the mock ProcessManager instance
        instance = mock_pm.return_value
        # the manager's procedures attribute holds created procedures and is
        # used for retrieval
        instance.procedures = procedures

        service = ScriptExecutionService()
        returned = service._get_subarray_id(
            process_pid
        )  # pylint: disable=protected-access

        assert returned == expected[0].script_args["init"].kwargs["subarray_id"]
Ejemplo n.º 30
0
def test_procedure_init_stores_initial_arguments(procedure):
    """
    Verify that the procedure constructor arguments are captured and persisted
    on the procedure instance.
    """
    assert procedure.script_args["init"] == ProcedureInput(1, 2, 3, kw1="a", kw2="b")