def test_server_session_timeout_async(self): application = Application() with ManagedServerLoop(application) as server: doc = document.Document() doc.add_root(DictModel()) client_session = push_session( doc, session_id='test_server_session_timeout_async', url=url(server), io_loop=server.io_loop) server_session = server.get_session('/', client_session.id) result = next(iter(server_session.document.roots)) @gen.coroutine def cb(): # we're testing that we can modify the doc and be # "inside" the document lock result.values['a'] = 0 result.values['b'] = yield self.async_value(1) result.values['c'] = yield self.async_value(2) result.values['d'] = yield self.async_value(3) result.values['e'] = yield self.async_value(4) client_session.close() raise gen.Return(5) cb_id = server_session.document.add_timeout_callback(cb, 10) client_session._loop_until_closed() with pytest.raises(ValueError) as exc: server_session.document.remove_timeout_callback(cb_id) assert 'already removed' in repr(exc.value) assert dict(a=0, b=1, c=2, d=3, e=4) == result.values
def test_lots_of_concurrent_messages(self, ManagedServerLoop): application = Application() def setup_stuff(doc): m1 = AnotherModelInTestClientServer(bar=43, name='m1') m2 = SomeModelInTestClientServer(foo=42, name='m2') m3 = SomeModelInTestClientServer(foo=68, name='m3') doc.add_root(m1) doc.add_root(m2) doc.add_root(m3) def timeout1(): m1.bar += 1 timeout1_cb_id = doc.add_timeout_callback(timeout1, 1) def timeout2(): m2.foo += 1 timeout2_cb_id = doc.add_timeout_callback(timeout2, 3) def periodic1(): m1.bar += 1 doc.remove_timeout_callback(timeout1_cb_id) doc.add_timeout_callback(timeout1, m1.bar % 7) doc.add_periodic_callback(periodic1, 3) def periodic2(): m2.foo += 1 doc.remove_timeout_callback(timeout2_cb_id) doc.add_timeout_callback(timeout2, m2.foo % 7) doc.add_periodic_callback(periodic2, 1) def server_on_change(event): if isinstance(event, ModelChangedEvent) and event.model is m3: return m3.foo += 1 doc.on_change(server_on_change) handler = FunctionHandler(setup_stuff) application.add(handler) # keep_alive_milliseconds=1 sends pings as fast as the OS will let us with ManagedServerLoop(application, keep_alive_milliseconds=1) as server: session = pull_session( session_id='test_lots_of_concurrent_messages', url=url(server), io_loop=server.io_loop) assert session.connected server_session = server.get_session('/', session.id) def client_timeout(): m = session.document.roots[0] m.name = m.name[::-1] cb_id = session.document.add_timeout_callback(client_timeout, 3) def client_periodic(): m = session.document.roots[1] m.name = m.name[::-1] session.document.remove_timeout_callback(cb_id) session.document.add_timeout_callback(client_timeout, 3) session.document.add_periodic_callback(client_periodic, 1) result = {} def end_test(): result['connected'] = session.connected result[ 'server_connection_count'] = server_session.connection_count result['server_close_code'] = next( iter(server._tornado._clients))._socket.close_code result['doc'] = session.document.to_json() session.close() # making this longer is more likely to trigger bugs, but it also # makes the test take however much time you put here session.document.add_timeout_callback(end_test, 250) def client_on_change(event): if not isinstance(event, TitleChangedEvent): session.document.title = session.document.title[::-1] session.document.on_change(client_on_change) session._loop_until_closed() assert not session.connected # we should have still been connected at the end, # if we didn't have any crazy protocol errors assert 'connected' in result assert result['connected'] # server should also still have been connected assert result['server_connection_count'] == 1 assert result['server_close_code'] is None
def test_server_changes_go_to_client(self, ManagedServerLoop): application = Application() with ManagedServerLoop(application) as server: doc = document.Document() client_session = push_session( doc, session_id='test_server_changes_go_to_client', url=url(server), io_loop=server.io_loop) server_session = server.get_session('/', client_session.id) assert len(client_session.document.roots) == 0 server_root = SomeModelInTestClientServer(foo=42) def do_add_server_root(): server_session.document.add_root(server_root) server.io_loop.spawn_callback(server_session.with_document_locked, do_add_server_root) def client_has_root(): return len(doc.roots) > 0 client_session._connection._loop_until(client_has_root) client_root = next(iter(client_session.document.roots)) assert client_root.foo == 42 assert server_root.foo == 42 # Now try setting title on server side def do_set_server_title(): server_session.document.title = "Server Title" server.io_loop.spawn_callback(server_session.with_document_locked, do_set_server_title) def client_title_set(): return client_session.document.title != document.DEFAULT_TITLE client_session._connection._loop_until(client_title_set) assert client_session.document.title == "Server Title" # Now modify a model within the server document def do_set_property_on_server(): server_root.foo = 57 server.io_loop.spawn_callback(server_session.with_document_locked, do_set_property_on_server) # there is no great way to block until the server # has applied changes, since patches are sent # asynchronously. We use internal _loop_until API. def client_change_made(): return client_root.foo == 57 client_session._connection._loop_until(client_change_made) assert client_root.foo == 57 def do_remove_server_root(): server_session.document.remove_root(server_root) server.io_loop.spawn_callback(server_session.with_document_locked, do_remove_server_root) def client_lacks_root(): return len(doc.roots) == 0 client_session._connection._loop_until(client_lacks_root) assert len(client_session.document.roots) == 0 client_session.close() client_session._loop_until_closed() assert not client_session.connected
async def check_http_gets(self, server): await http_get(server.io_loop, url(server)) await http_get(server.io_loop, url(server) + "autoload.js?bokeh-autoload-element=foo")
def test_unit_spec_changes_do_not_boomerang(monkeypatch, ManagedServerLoop): application = Application() with ManagedServerLoop(application) as server: doc = document.Document() client_root = UnitsSpecModel() doc.add_root(client_root) client_session = push_session( doc, session_id='test_unit_spec_changes_do_not_boomerang', url=url(server), io_loop=server.io_loop) server_session = server.get_session('/', client_session.id) assert len(server_session.document.roots) == 1 server_root = next(iter(server_session.document.roots)) assert client_root.distance == 42 assert server_root.angle == 0 def change_to(new_distance, new_angle): got_angry = {} got_angry['result'] = None # trap any boomerang def get_angry(message): got_angry['result'] = message monkeypatch.setattr(client_session, '_handle_patch', get_angry) server_previous_distance = server_root.distance server_previous_angle = server_root.angle # Now modify the client document client_root.distance = new_distance client_root.angle = new_angle # wait until server side change made ... but we may not have the # boomerang yet def server_change_made(): return server_root.distance != server_previous_distance and \ server_root.angle != server_previous_angle client_session._connection._loop_until(server_change_made) # force a round trip to be sure we get the boomerang if we're going to client_session.force_roundtrip() assert got_angry['result'] is None change_to(57, 1) change_to({'value': 58}, {'value': 2}) change_to({'field': 'foo'}, {'field': 'bar'}) change_to({ 'value': 59, 'units': 'screen' }, { 'value': 30, 'units': 'deg' }) client_session.close() client_session._loop_until_closed() assert not client_session.connected server.unlisten() # clean up so next test can run
async def check_http_gets_fail(self, server): with pytest.raises(HTTPError): await http_get(server.io_loop, url(server)) with pytest.raises(HTTPError): await http_get(server.io_loop, url(server) + "autoload.js?bokeh-autoload-element=foo")