示例#1
0
 def test_unique_id(self, _1):
     """Each AppSession should have a unique ID"""
     file_mgr = MagicMock(spec=UploadedFileManager)
     lsw = MagicMock()
     rs1 = AppSession(None, SessionData("", ""), file_mgr, None, lsw)
     rs2 = AppSession(None, SessionData("", ""), file_mgr, None, lsw)
     self.assertNotEqual(rs1.id, rs2.id)
示例#2
0
    def _create_app_session(self, ws: WebSocketHandler) -> AppSession:
        """Register a connected browser with the server.

        Parameters
        ----------
        ws : _BrowserWebSocketHandler
            The newly-connected websocket handler.

        Returns
        -------
        AppSession
            The newly-created AppSession for this browser connection.

        """
        session_data = SessionData(self._main_script_path, self._command_line)
        local_sources_watcher = LocalSourcesWatcher(session_data)
        session = AppSession(
            ioloop=self._ioloop,
            session_data=session_data,
            uploaded_file_manager=self._uploaded_file_mgr,
            message_enqueued_callback=self._enqueued_some_message,
            local_sources_watcher=local_sources_watcher,
        )

        LOGGER.debug("Created new session for ws %s. Session ID: %s", id(ws),
                     session.id)

        assert (session.id not in self._session_info_by_id
                ), f"session.id '{session.id}' registered multiple times!"

        self._session_info_by_id[session.id] = SessionInfo(ws, session)
        self._set_state(State.ONE_OR_MORE_BROWSERS_CONNECTED)
        self._has_connection.notify_all()

        return session
示例#3
0
    async def does_script_run_without_error(self) -> Tuple[bool, str]:
        """Load and execute the app's script to verify it runs without an error.

        Returns
        -------
        (True, "ok") if the script completes without error, or (False, err_msg)
        if the script raises an exception.
        """
        session_data = SessionData(self._main_script_path, self._command_line)
        local_sources_watcher = LocalSourcesWatcher(session_data)
        session = AppSession(
            ioloop=self._ioloop,
            session_data=session_data,
            uploaded_file_manager=self._uploaded_file_mgr,
            message_enqueued_callback=self._enqueued_some_message,
            local_sources_watcher=local_sources_watcher,
        )

        try:
            session.request_rerun(None)

            now = time.perf_counter()
            while (SCRIPT_RUN_WITHOUT_ERRORS_KEY not in session.session_state
                   and (time.perf_counter() - now) < SCRIPT_RUN_CHECK_TIMEOUT):
                await tornado.gen.sleep(0.1)

            if SCRIPT_RUN_WITHOUT_ERRORS_KEY not in session.session_state:
                return False, "timeout"

            ok = session.session_state[SCRIPT_RUN_WITHOUT_ERRORS_KEY]
            msg = "ok" if ok else "error"

            return ok, msg
        finally:
            session.shutdown()
示例#4
0
    def test_enqueue_without_tracer(self, _1, _2, patched_config):
        """Make sure we try to handle execution control requests."""

        def get_option(name):
            if name == "server.runOnSave":
                # Just to avoid starting the watcher for no reason.
                return False
            if name == "client.displayEnabled":
                return True
            if name == "runner.installTracer":
                return False
            raise RuntimeError("Unexpected argument to get_option: %s" % name)

        patched_config.get_option.side_effect = get_option

        send = MagicMock()
        rs = AppSession(
            None, SessionData("", ""), UploadedFileManager(), send, MagicMock()
        )
        mock_script_runner = MagicMock()
        mock_script_runner._install_tracer = ScriptRunner._install_tracer
        rs._scriptrunner = mock_script_runner

        mock_msg = MagicMock()
        rs.enqueue(mock_msg)

        func = mock_script_runner.maybe_handle_execution_control_request

        # Expect func and send to be called only once, inside enqueue().
        func.assert_called_once()
        send.assert_called_once()
示例#5
0
 def test_clear_cache_resets_session_state(self, _1):
     rs = AppSession(
         None, SessionData("", ""), UploadedFileManager(), None, MagicMock()
     )
     rs._session_state["foo"] = "bar"
     rs.handle_clear_cache_request()
     self.assertTrue("foo" not in rs._session_state)
示例#6
0
def _create_test_session() -> AppSession:
    """Create an AppSession instance with some default mocked data."""
    return AppSession(
        ioloop=MagicMock(),
        session_data=SessionData("/fake/script_path", "fake_command_line"),
        uploaded_file_manager=MagicMock(),
        message_enqueued_callback=lambda: None,
        local_sources_watcher=MagicMock(),
    )
示例#7
0
    def test_passes_client_state_on_run_on_save(self, _):
        rs = AppSession(
            None, SessionData("", ""), UploadedFileManager(), None, MagicMock()
        )
        rs._run_on_save = True
        rs.request_rerun = MagicMock()
        rs._on_source_file_changed()

        rs.request_rerun.assert_called_once_with(rs._client_state)
示例#8
0
 def test_clear_cache_all_caches(
     self, clear_singleton_cache, clear_memo_cache, clear_legacy_cache
 ):
     rs = AppSession(
         MagicMock(), SessionData("", ""), UploadedFileManager(), None, MagicMock()
     )
     rs.handle_clear_cache_request()
     clear_singleton_cache.assert_called_once()
     clear_memo_cache.assert_called_once()
     clear_legacy_cache.assert_called_once()
示例#9
0
    def test_enqueue_new_session_message(self, _1, _2, patched_config):
        def get_option(name):
            if name == "server.runOnSave":
                # Just to avoid starting the watcher for no reason.
                return False

            return config.get_option(name)

        patched_config.get_option.side_effect = get_option
        patched_config.get_options_for_section.side_effect = (
            _mock_get_options_for_section()
        )

        # Create a AppSession with some mocked bits
        rs = AppSession(
            self.io_loop,
            SessionData("mock_report.py", ""),
            UploadedFileManager(),
            lambda: None,
            MagicMock(),
        )

        orig_ctx = get_script_run_ctx()
        ctx = ScriptRunContext(
            "TestSessionID", rs._session_data.enqueue, "", None, None
        )
        add_script_run_ctx(ctx=ctx)

        rs._on_scriptrunner_event(ScriptRunnerEvent.SCRIPT_STARTED)

        sent_messages = rs._session_data._browser_queue._queue
        self.assertEqual(len(sent_messages), 2)  # NewApp and SessionState messages

        # Note that we're purposefully not very thoroughly testing new_session
        # fields below to avoid getting to the point where we're just
        # duplicating code in tests.
        new_session_msg = sent_messages[0].new_session
        self.assertEqual("mock_scriptrun_id", new_session_msg.script_run_id)

        self.assertEqual(new_session_msg.HasField("config"), True)
        self.assertEqual(
            new_session_msg.config.allow_run_on_save,
            config.get_option("server.allowRunOnSave"),
        )

        self.assertEqual(new_session_msg.HasField("custom_theme"), True)
        self.assertEqual(new_session_msg.custom_theme.text_color, "black")

        init_msg = new_session_msg.initialize
        self.assertEqual(init_msg.HasField("user_info"), True)

        add_script_run_ctx(ctx=orig_ctx)
示例#10
0
    def test_shutdown(self, _, patched_disconnect):
        """Test that AppSession.shutdown behaves sanely."""
        file_mgr = MagicMock(spec=UploadedFileManager)
        rs = AppSession(None, SessionData("", ""), file_mgr, None, MagicMock())

        rs.shutdown()
        self.assertEqual(AppSessionState.SHUTDOWN_REQUESTED, rs._state)
        file_mgr.remove_session_files.assert_called_once_with(rs.id)
        patched_disconnect.assert_called_once_with(rs._on_secrets_file_changed)

        # A 2nd shutdown call should have no effect.
        rs.shutdown()
        self.assertEqual(AppSessionState.SHUTDOWN_REQUESTED, rs._state)
        file_mgr.remove_session_files.assert_called_once_with(rs.id)
示例#11
0
    def test_shutdown(self, patched_disconnect):
        """Test that AppSession.shutdown behaves sanely."""
        file_mgr = MagicMock(spec=UploadedFileManager)
        session = AppSession(
            ioloop=MagicMock(),
            session_data=SessionData("", ""),
            uploaded_file_manager=file_mgr,
            message_enqueued_callback=None,
            local_sources_watcher=MagicMock(),
        )

        session.shutdown()
        self.assertEqual(AppSessionState.SHUTDOWN_REQUESTED, session._state)
        file_mgr.remove_session_files.assert_called_once_with(session.id)
        patched_disconnect.assert_called_once_with(session._on_secrets_file_changed)

        # A 2nd shutdown call should have no effect.
        session.shutdown()
        self.assertEqual(AppSessionState.SHUTDOWN_REQUESTED, session._state)
        file_mgr.remove_session_files.assert_called_once_with(session.id)
示例#12
0
    def test_enqueue_with_tracer(self, _1, _2, patched_config, _4):
        """Make sure there is no lock contention when tracer is on.

        When the tracer is set up, we want
        maybe_handle_execution_control_request to be executed only once. There
        was a bug in the past where it was called twice: once from the tracer
        and once from the enqueue function. This caused a lock contention.
        """

        def get_option(name):
            if name == "server.runOnSave":
                # Just to avoid starting the watcher for no reason.
                return False
            if name == "client.displayEnabled":
                return True
            if name == "runner.installTracer":
                return True
            raise RuntimeError("Unexpected argument to get_option: %s" % name)

        patched_config.get_option.side_effect = get_option

        rs = AppSession(
            None, SessionData("", ""), UploadedFileManager(), lambda: None, MagicMock()
        )
        mock_script_runner = MagicMock()
        rs._scriptrunner = mock_script_runner

        mock_msg = MagicMock()
        rs.enqueue(mock_msg)

        func = mock_script_runner.maybe_handle_execution_control_request

        # In reality, outside of a testing environment func should be called
        # once. But in this test we're actually not installing a tracer here,
        # since SessionData is mocked. So the correct behavior here is for func to
        # never be called. If you ever see it being called once here it's
        # likely because there's a bug in the enqueue function (which should
        # skip func when installTracer is on).
        func.assert_not_called()
示例#13
0
    def test_enqueue_new_session_message(self, patched_config):
        """The SCRIPT_STARTED event should enqueue a 'new_session' message."""

        def get_option(name):
            if name == "server.runOnSave":
                # Just to avoid starting the watcher for no reason.
                return False

            return config.get_option(name)

        patched_config.get_option.side_effect = get_option
        patched_config.get_options_for_section.side_effect = (
            _mock_get_options_for_section()
        )

        # Create a AppSession with some mocked bits
        session = AppSession(
            ioloop=self.io_loop,
            session_data=SessionData("mock_report.py", ""),
            uploaded_file_manager=UploadedFileManager(),
            message_enqueued_callback=lambda: None,
            local_sources_watcher=MagicMock(),
        )

        orig_ctx = get_script_run_ctx()
        ctx = ScriptRunContext(
            session_id="TestSessionID",
            enqueue=session._session_data.enqueue,
            query_string="",
            session_state=MagicMock(),
            uploaded_file_mgr=MagicMock(),
        )
        add_script_run_ctx(ctx=ctx)

        # Send a mock SCRIPT_STARTED event.
        session._on_scriptrunner_event(
            sender=MagicMock(), event=ScriptRunnerEvent.SCRIPT_STARTED
        )

        sent_messages = session._session_data._browser_queue._queue
        self.assertEqual(2, len(sent_messages))  # NewApp and SessionState messages

        # Note that we're purposefully not very thoroughly testing new_session
        # fields below to avoid getting to the point where we're just
        # duplicating code in tests.
        new_session_msg = sent_messages[0].new_session
        self.assertEqual("mock_scriptrun_id", new_session_msg.script_run_id)

        self.assertTrue(new_session_msg.HasField("config"))
        self.assertEqual(
            config.get_option("server.allowRunOnSave"),
            new_session_msg.config.allow_run_on_save,
        )

        self.assertTrue(new_session_msg.HasField("custom_theme"))
        self.assertEqual("black", new_session_msg.custom_theme.text_color)

        init_msg = new_session_msg.initialize
        self.assertTrue(init_msg.HasField("user_info"))

        add_script_run_ctx(ctx=orig_ctx)
示例#14
0
 def test_request_rerun_on_secrets_file_change(self, patched_connect):
     rs = AppSession(
         None, SessionData("", ""), UploadedFileManager(), None, MagicMock()
     )
     patched_connect.assert_called_once_with(rs._on_secrets_file_changed)
示例#15
0
 def test_creates_session_state_on_init(self, _):
     rs = AppSession(
         None, SessionData("", ""), UploadedFileManager(), None, MagicMock()
     )
     self.assertTrue(isinstance(rs.session_state, SessionState))