Пример #1
0
    def test_stop_during_init_sets_lifecycle_state_to_stopped(
            self, manager, init_hang_script):
        """
        Verify that procedure terminate changes to STOPPED
        when terminate() is called
        """
        helper = PubSubHelper()

        init_running = multiprocessing.Barrier(2)
        init_args = ProcedureInput(init_running)

        pid = manager.create(init_hang_script, init_args=init_args)

        init_running.wait(0.1)
        wait_for_empty_message_queue(manager)
        manager.stop(pid)

        expected = [
            ProcedureState.CREATING,  # ScriptWorker initialising
            ProcedureState.IDLE,  # ScriptWorker ready
            ProcedureState.LOADING,  # load user module
            ProcedureState.IDLE,  # user module loaded
            ProcedureState.RUNNING,  # init running
            ProcedureState.STOPPED,  # init stopped
        ]
        helper.wait_for_lifecycle(ProcedureState.STOPPED)
        self.assert_states(helper, pid, expected)
Пример #2
0
    def test_internal_messages_not_republished(self, mp, caplog):
        """
        Verify that message event is not published if the event originates from
        an internal source.
        """
        helper = PubSubHelper()

        work_q = MPQueue(ctx=mp)
        # TEST is the default component name assigned in
        # _proc_worker_wrapper_helper. This message should not be published to pypubsub
        msg = EventMessage(
            "TEST",
            "PUBSUB",
            dict(topic=topics.request.procedure.list,
                 kwargs={"request_id": "123"}),
        )
        work_q.put(msg)

        _proc_worker_wrapper_helper(mp,
                                    caplog,
                                    ScriptWorker,
                                    args=(work_q, ),
                                    expect_shutdown_evt=True)

        msgs_on_topic = helper.messages_on_topic(topics.request.procedure.list)
        assert len(msgs_on_topic) == 0
Пример #3
0
    def test_stop_during_main_sets_lifecycle_state_to_stopped(
            self, manager, main_hang_script):
        """
        Verify that procedure state changes to STOPPED
        when terminate() is called
        """
        helper = PubSubHelper()

        main_running = multiprocessing.Barrier(2)
        init_args = ProcedureInput(main_running)

        pid = manager.create(main_hang_script, init_args=init_args)
        helper.wait_for_lifecycle(ProcedureState.READY)
        manager.run(pid, call="main", run_args=ProcedureInput())
        main_running.wait(0.5)

        helper.wait_for_lifecycle(ProcedureState.RUNNING)
        manager.stop(pid)
        helper.wait_for_lifecycle(ProcedureState.STOPPED)

        expected = [
            ProcedureState.CREATING,  # ScriptWorker initialising
            ProcedureState.IDLE,  # ScriptWorker ready
            ProcedureState.LOADING,  # load user module
            ProcedureState.IDLE,  # user module loaded
            ProcedureState.RUNNING,  # init running
            ProcedureState.READY,  # init complete
            ProcedureState.RUNNING,  # main running
            ProcedureState.STOPPED,  # main stopped
        ]
        self.assert_states(helper, pid, expected)
Пример #4
0
    def test_events_emitted_from_scripts_are_republished(
            self, manager, pubsub_script):
        """
        Verify that OET events are published at the appropriate times for a
        sad-path script.
        """
        helper = PubSubHelper()

        pid = manager.create(pubsub_script, init_args=ProcedureInput())
        wait_for_state(manager, pid, ProcedureState.READY)

        manager.run(pid, call="main", run_args=ProcedureInput(msg="foo"))
        helper.wait_for_message_on_topic(topics.user.script.announce)
        user_msgs = helper.messages_on_topic(topics.user.script.announce)
        assert len(user_msgs) == 1
    def test_external_messages_are_published_locally(self, mp_fixture, caplog):
        """
        Verify that message event is published if the event originates from an
        external source.
        """
        pubsub.pub.unsubAll()
        helper = PubSubHelper()

        work_q = MPQueue(ctx=mp_fixture)
        msg = EventMessage(
            "EXTERNAL COMPONENT",
            "PUBSUB",
            dict(topic=topics.request.procedure.list,
                 kwargs={"request_id": "123"}),
        )
        work_q.put(msg)

        with mock.patch.object(pubsub.pub, "unsubAll", return_value=[]):
            _proc_worker_wrapper_helper(
                mp_fixture,
                caplog,
                EventBusWorker,
                args=(work_q, ),
                expect_shutdown_evt=True,
            )

        assert topics.request.procedure.list in helper.topic_list
        work_q.safe_close()
def assert_command_request_and_response(mp_fixture, caplog, mock_method,
                                        request_topic, response_topic, cmd):
    pubsub.pub.unsubAll()
    helper = PubSubHelper()

    work_q = MPQueue(ctx=mp_fixture)
    msg = EventMessage(
        "UNITTEST",
        "PUBSUB",
        dict(topic=request_topic, kwargs={
            "request_id": "1234",
            "cmd": cmd
        }),
    )
    work_q.put(msg)
    event = mp_fixture.Event()

    mock_method.side_effect = partial(set_event, event)
    with mock.patch.object(pubsub.pub, "unsubAll", return_value=[]):
        _proc_worker_wrapper_helper(
            mp_fixture,
            caplog,
            ScriptExecutionServiceWorker,
            args=(work_q, mp_fixture),
            expect_shutdown_evt=True,
        )

    assert event.is_set()
    mock_method.assert_called_once()
    assert mock_method.call_args[0][0] == cmd

    assert helper.topic_list == [request_topic, response_topic]

    work_q.safe_close()
Пример #7
0
 def assert_states(helper: PubSubHelper, pid: int,
                   expected: List[ProcedureState]):
     msgs = helper.messages_on_topic(topics.procedure.lifecycle.statechange)
     states = [
         msg["new_state"] for msg in msgs if int(msg["msg_src"]) == pid
     ]
     assert states == expected
Пример #8
0
    def test_happy_path_script_execution_lifecycle_states(
            self, manager: ProcessManager, barrier_script):
        """
        Verify that a new ScriptWorker sends the appropriate lifecycle states.

        The mptools ProcWorker tests already verify that events are set at the
        appropriate times. This test is to confirm that lifecycle EventMessages
        are sent at the appropriate times too.
        """
        helper = PubSubHelper()

        init_running = multiprocessing.Barrier(2)
        main_running = multiprocessing.Barrier(2)
        resume = multiprocessing.Barrier(2)
        init_args = ProcedureInput(init_running, main_running, resume)

        pid = manager.create(barrier_script, init_args=init_args)
        init_running.wait(0.1)
        wait_for_state(manager, pid, ProcedureState.RUNNING)
        expected = [
            ProcedureState.CREATING,  # ScriptWorker initialising
            ProcedureState.IDLE,  # ScriptWorker ready
            ProcedureState.LOADING,  # load user module
            ProcedureState.IDLE,  # user module loaded
            ProcedureState.RUNNING,  # init present and called
        ]
        self.assert_states(helper, pid, expected)

        # let init complete, then check for completion
        resume.wait(0.1)
        resume.reset()  # reset to pause main method call
        wait_for_state(manager, pid, ProcedureState.READY)
        expected.append(ProcedureState.READY)  # init complete
        self.assert_states(helper, pid, expected)

        # now set main running
        manager.run(pid, call="main", run_args=ProcedureInput())
        expected.append(ProcedureState.RUNNING)  # main running
        main_running.wait(0.1)
        wait_for_state(manager, pid, ProcedureState.RUNNING)
        self.assert_states(helper, pid, expected)

        # wait for ScriptWorker process to complete
        resume.wait(0.1)
        resume.reset()  # reset to pause main method call
        wait_for_state(manager, pid, ProcedureState.COMPLETE)
        expected.extend([
            ProcedureState.READY,  # main complete
            ProcedureState.COMPLETE,  # script complete
        ])
        self.assert_states(helper, pid, expected)
Пример #9
0
    def test_shared_environment_sys_path_is_set(self, mock_subprocess_fn,
                                                mock_clone_fn,
                                                git_sys_path_script, manager):
        """
        Verify site packages are added to sys.path correctly for scripts sharing an environment.
        """
        site_pkg = "/python/site_packages"
        env = Environment(
            "123",
            multiprocessing.Event(),
            multiprocessing.Event(),
            "/",
            site_pkg,
        )
        manager.em.create_env = MagicMock()
        manager.em.create_env.side_effect = [env, env]

        # Return path to git file from clone call in env creation and module load
        mock_clone_fn.return_value = "/"

        pid1 = manager.create(git_sys_path_script,
                              init_args=ProcedureInput(site_pkg))
        pid2 = manager.create(git_sys_path_script,
                              init_args=ProcedureInput(site_pkg))
        wait_for_state(manager, pid1, ProcedureState.READY)
        wait_for_state(manager, pid2, ProcedureState.READY)

        # Running the main function asserts the site_pkg is in sys.path
        # If assertion fails, script state goes to FAILED, else it goes
        # to COMPLETE
        helper = PubSubHelper()
        manager.run(pid1, call="main", run_args=ProcedureInput(site_pkg))
        assert helper.wait_for_lifecycle(ProcedureState.COMPLETE, msg_src=pid1)

        manager.run(pid2, call="main", run_args=ProcedureInput(site_pkg))
        assert helper.wait_for_lifecycle(ProcedureState.COMPLETE, msg_src=pid2)
Пример #10
0
    def test_error_in_main_lifecycles_states(self, manager: ProcessManager,
                                             fail_script):
        helper = PubSubHelper()

        pid = manager.create(fail_script, init_args=ProcedureInput())
        wait_for_state(manager, pid, ProcedureState.READY)

        manager.run(pid, call="main", run_args=ProcedureInput("foo"))

        wait_for_state(manager, pid, ProcedureState.FAILED)
        expected = [
            ProcedureState.CREATING,  # ScriptWorker initialising
            ProcedureState.IDLE,  # ScriptWorker ready
            ProcedureState.LOADING,  # load user module
            ProcedureState.IDLE,  # user module loaded
            # fail script has no init so no IDLE->READY expected
            ProcedureState.READY,  # init complete
            ProcedureState.RUNNING,  # main running
            ProcedureState.FAILED,  # exception raised
        ]
        helper.wait_for_lifecycle(ProcedureState.FAILED)
        # wait_for_state(manager, pid, ProcedureState.FAILED)
        # helper.wait_for_message_on_topic(topics.procedure.lifecycle.stacktrace)
        self.assert_states(helper, pid, expected)
    def test_internal_messages_not_republished(self, mp_fixture, caplog):
        """
        Verify that message event is not published if the event originates from
        an internal source.
        """
        pubsub.pub.unsubAll()
        helper = PubSubHelper()

        work_q = MPQueue(ctx=mp_fixture)
        # TEST is the default component name assigned in
        # _proc_worker_wrapper_helper. This message should be ignored.
        msg = EventMessage(
            "TEST",
            "PUBSUB",
            dict(topic=topics.request.procedure.list,
                 kwargs={"request_id": "123"}),
        )

        work_q.put(msg)
        # But coming from NONTEST, this message should be republished.
        msg = EventMessage(
            "NONTEST",
            "PUBSUB",
            dict(topic=topics.request.procedure.list,
                 kwargs={"request_id": "456"}),
        )
        work_q.put(msg)

        with mock.patch.object(pubsub.pub, "unsubAll", return_value=[]):
            _proc_worker_wrapper_helper(
                mp_fixture,
                caplog,
                EventBusWorker,
                args=(work_q, ),
                expect_shutdown_evt=True,
            )

        assert len(helper.messages) == 1
        assert helper.messages[0][1] == dict(msg_src="NONTEST",
                                             request_id="456")

        work_q.safe_close()
    def test_handles_request_to_list_invalid_id(self, mp_fixture, caplog):
        """
        The ValueError raised when SES.summarise is given an invalid PID should be handled.
        """
        pubsub.pub.unsubAll()
        helper = PubSubHelper()

        work_q = MPQueue(ctx=mp_fixture)
        msg = EventMessage(
            "TEST_SUMMARY",
            "PUBSUB",
            dict(topic=topics.request.procedure.list,
                 kwargs={"request_id": "123"}),
        )
        work_q.put(msg)

        with mock.patch(
                "ska_oso_oet.procedure.application.main.ScriptExecutionService.summarise"
        ) as mock_cls:
            with mock.patch.object(pubsub.pub, "unsubAll", return_value=[]):
                mock_cls.side_effect = ValueError
                _proc_worker_wrapper_helper(
                    mp_fixture,
                    caplog,
                    ScriptExecutionServiceWorker,
                    args=(work_q, mp_fixture),
                    expect_shutdown_evt=True,
                )

        mock_cls.assert_called_once()

        assert helper.topic_list == [
            topics.request.procedure.list,  # list requested
            topics.procedure.pool.list,  # response published
        ]
        assert helper.messages[1][1] == dict(msg_src="TEST",
                                             request_id="123",
                                             result=[])

        work_q.safe_close()
    def test_list_method_called(self, mp_fixture, caplog):
        """
        SES.summarise should be called when 'request.procedure.list' message is received
        """
        pubsub.pub.unsubAll()
        helper = PubSubHelper()

        work_q = MPQueue(ctx=mp_fixture)
        msg = EventMessage(
            "TEST_SUMMARY",
            "PUBSUB",
            dict(topic=topics.request.procedure.list,
                 kwargs={"request_id": "123"}),
        )
        work_q.put(msg)
        event = mp_fixture.Event()

        with mock.patch(
                "ska_oso_oet.procedure.application.main.ScriptExecutionService.summarise"
        ) as mock_cls:
            with mock.patch.object(pubsub.pub, "unsubAll", return_value=[]):
                mock_cls.side_effect = partial(set_event, event)
                _proc_worker_wrapper_helper(
                    mp_fixture,
                    caplog,
                    ScriptExecutionServiceWorker,
                    args=(work_q, mp_fixture),
                    expect_shutdown_evt=True,
                )

        assert event.is_set() is True
        mock_cls.assert_called_once()

        assert helper.topic_list == [
            topics.request.procedure.list,  # list requested
            topics.procedure.pool.list,  # response published
        ]

        work_q.safe_close()
Пример #14
0
    def test_stop_terminates_the_process(self, manager, abort_script):
        """
        Verify that ProcessManager stops a script execution
        """
        helper = PubSubHelper()
        with Manager() as mgr:
            q = mgr.Queue()
            is_running = multiprocessing.Barrier(2)
            pid = manager.create(abort_script,
                                 init_args=ProcedureInput(q, is_running))
            wait_for_state(manager, pid, ProcedureState.READY)
            manager.run(pid, call="main", run_args=ProcedureInput())

            is_running.wait(0.1)
            helper.wait_for_lifecycle(ProcedureState.RUNNING)
            manager.stop(pid)
            helper.wait_for_lifecycle(ProcedureState.STOPPED)
            assert manager.running is None
            assert q.empty()
    def test_stop_events(self, ses, init_hang_script):
        """
        Verify the behaviour of SES.stop(), confirming that OET events are published
        at the appropriate times when a script is terminated.
        """
        helper = PubSubHelper()

        init_running = multiprocessing.Barrier(2)
        prepare_cmd = PrepareProcessCommand(
            script=init_hang_script, init_args=ProcedureInput(init_running))
        summary = ses.prepare(prepare_cmd)
        pid = summary.id
        ses._wait_for_state(pid, ProcedureState.IDLE)

        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.created)) == 1
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.started)) == 1
        assert len(
            helper.messages_on_topic(topics.procedure.lifecycle.complete)) == 0
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.failed)) == 0
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.stopped)) == 0

        init_running.wait(0.1)
        ses._wait_for_state(pid, ProcedureState.READY)

        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.created)) == 1
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.started)) == 1
        assert len(
            helper.messages_on_topic(topics.procedure.lifecycle.complete)) == 0
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.failed)) == 0
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.stopped)) == 0

        stop_cmd = StopProcessCommand(process_uid=pid, run_abort=False)
        _ = ses.stop(stop_cmd)
        ses._wait_for_state(pid, ProcedureState.STOPPED)

        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.created)) == 1
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.started)) == 1
        assert len(
            helper.messages_on_topic(topics.procedure.lifecycle.complete)) == 0
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.failed)) == 0
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.stopped)) == 1

        filtered = [
            t for t in helper.topic_list
            if t != topics.procedure.lifecycle.statechange
        ]
        assert filtered == [
            topics.procedure.lifecycle.created,
            topics.procedure.lifecycle.started,
            topics.procedure.lifecycle.stopped,
        ]
    def test_sad_path_events(self, ses, fail_script):
        """
        Verify that OET events are published at the appropriate times for a
        script that raises an exception.
        """
        helper = PubSubHelper()

        prepare_cmd = PrepareProcessCommand(script=fail_script,
                                            init_args=ProcedureInput())
        summary = ses.prepare(prepare_cmd)

        helper.wait_for_message_on_topic(topics.procedure.lifecycle.created,
                                         timeout=3.0)
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.created)) == 1
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.started)) == 0
        assert len(
            helper.messages_on_topic(topics.procedure.lifecycle.complete)) == 0
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.failed)) == 0
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.stopped)) == 0

        run_cmd = StartProcessCommand(process_uid=summary.id,
                                      fn_name="main",
                                      run_args=ProcedureInput(msg="foo"))
        _ = ses.start(run_cmd)

        helper.wait_for_message_on_topic(topics.procedure.lifecycle.stacktrace,
                                         timeout=3.0)
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.created)) == 1
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.started)) == 1
        assert len(
            helper.messages_on_topic(topics.procedure.lifecycle.complete)) == 0
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.failed)) == 1
        assert len(helper.messages_on_topic(
            topics.procedure.lifecycle.stopped)) == 0

        filtered = [
            t for t in helper.topic_list
            if t != topics.procedure.lifecycle.statechange
        ]
        assert filtered == [
            topics.procedure.lifecycle.created,
            topics.procedure.lifecycle.started,
            topics.procedure.lifecycle.failed,
            topics.procedure.lifecycle.stacktrace,
        ]