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)
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
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()
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()
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)
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(), )
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)
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()
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)
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)
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)
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()
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)
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)
def test_creates_session_state_on_init(self, _): rs = AppSession( None, SessionData("", ""), UploadedFileManager(), None, MagicMock() ) self.assertTrue(isinstance(rs.session_state, SessionState))