Example #1
0
    def test_plot_dict_returned_when_wrap_plot_info_is_false(
            self, mock_make_id: MagicMock) -> None:
        doc = Document()
        plot1 = figure()
        plot1.circle([], [])
        doc.add_root(plot1)

        plot2 = figure()
        plot2.circle([], [])
        doc.add_root(plot2)

        expected_plotdict_1 = RenderRoot(elementid=ID("ID"), id=ID("ID"))
        expected_plotdict_2 = RenderRoot(elementid=ID("ID"), id=ID("ID"))

        _, plotdict = bes.components(plot1, wrap_plot_info=False)
        assert plotdict == expected_plotdict_1

        _, plotids = bes.components((plot1, plot2), wrap_plot_info=False)
        assert plotids == (expected_plotdict_1, expected_plotdict_2)

        _, plotiddict = bes.components({
            'p1': plot1,
            'p2': plot2
        },
                                       wrap_plot_info=False)
        assert plotiddict == {
            'p1': expected_plotdict_1,
            'p2': expected_plotdict_2
        }
Example #2
0
 def test_session_callbacks(self) -> None:
     d = Document()
     cm = bdc.DocumentCallbackManager(d)
     assert set(cm.session_callbacks) == set()
     s1 = SessionCallback(lambda: None, callback_id=ID("1"))
     cm._session_callbacks.add(s1)
     assert set(cm.session_callbacks) == {s1}
     s2 = SessionCallback(lambda: None, callback_id=ID("2"))
     cm._session_callbacks.add(s2)
     assert set(cm.session_callbacks) == {s1, s2}
Example #3
0
def test_log_stats(ManagedServerLoop: MSL) -> None:
    application = Application()
    with ManagedServerLoop(application) as server:
        server._tornado._log_stats()
        session1 = pull_session(session_id=ID("session1"),
                                url=url(server),
                                io_loop=server.io_loop)
        session2 = pull_session(session_id=ID("session2"),
                                url=url(server),
                                io_loop=server.io_loop)
        server._tornado._log_stats()
        session1.close()
        session2.close()
        server._tornado._log_stats()
    def test_push_document(self, ManagedServerLoop: MSL) -> None:
        application = Application()
        with ManagedServerLoop(application) as server:
            doc = document.Document()
            doc.add_root(AnotherModelInTestClientServer(bar=43))
            doc.add_root(SomeModelInTestClientServer(foo=42))

            client_session = push_session(doc,
                                          session_id=ID("test_push_document"),
                                          url=url(server),
                                          io_loop=server.io_loop)

            assert client_session.document == doc
            assert len(client_session.document.roots) == 2

            server_session = server.get_session('/', client_session.id)

            assert len(server_session.document.roots) == 2
            results = {}
            for r in server_session.document.roots:
                if hasattr(r, 'foo'):
                    results['foo'] = r.foo
                if hasattr(r, 'bar'):
                    results['bar'] = r.bar
            assert results['foo'] == 42
            assert results['bar'] == 43

            client_session.close()
            client_session._loop_until_closed()
            assert not client_session.connected
Example #5
0
async def test_multiple_validation_success_with_multiple_buffers() -> None:
    r = receiver.Receiver(proto)

    for N in range(10):
        partial = await r.consume(
            f'{{"msgtype": "PATCH-DOC", "msgid": "10", "num_buffers":{N}}}')
        partial = await r.consume('{}')
        partial = await r.consume('{"bar": 10}')

        for i in range(N):
            partial = await r.consume(f'{{"id": "header{i}"}}')
            partial = await r.consume(f'payload{i}'.encode())

        assert partial is not None
        assert partial.msgtype == "PATCH-DOC"
        assert partial.header == {
            "msgtype": "PATCH-DOC",
            "msgid": "10",
            "num_buffers": N
        }
        assert partial.content == {"bar": 10}
        assert partial.metadata == {}
        for i in range(N):
            assert partial.buffers[i] == Buffer(ID(f"header{i}"),
                                                f"payload{i}".encode())
Example #6
0
    def test_client_session_periodic_async_added_before_push(
            self, ManagedServerLoop: MSL) -> None:
        application = Application()
        with ManagedServerLoop(application) as server:
            doc = document.Document()

            result = DictModel()
            doc.add_root(result)

            async def cb():
                result.values['a'] = 0
                result.values['b'] = await self.async_value(1)
                result.values['c'] = await self.async_value(2)
                result.values['d'] = await self.async_value(3)
                result.values['e'] = await self.async_value(4)
                client_session.close()
                return 5

            cb_id = doc.add_periodic_callback(cb, 10)

            client_session = push_session(
                doc,
                session_id=ID("test_client_session_periodic_async"),
                url=url(server),
                io_loop=server.io_loop)

            client_session._loop_until_closed()

            doc.remove_periodic_callback(cb_id)

            assert dict(a=0, b=1, c=2, d=3, e=4) == result.values
Example #7
0
    def test_remove_session_callback(self) -> None:
        d = Document()
        cm = bdc.DocumentCallbackManager(d)

        events = []

        def listener(event: DocumentChangedEvent) -> None:
            events.append(event)

        cm.on_change(listener)

        assert len(cm.session_callbacks) == 0
        assert not events

        def cb() -> None:
            pass

        obj = SessionCallback(cb, callback_id=ID("1"))

        cm.add_session_callback(obj, cb, one_shot=False)

        cm.remove_session_callback(obj)
        assert len(cm.session_callbacks) == 0
        assert len(events) == 2
        assert isinstance(events[0], SessionCallbackAdded)
        assert isinstance(events[1], SessionCallbackRemoved)
Example #8
0
async def test_validation_success_with_one_buffer() -> None:
    r = receiver.Receiver(proto)

    partial = await r.consume(
        '{"msgtype": "PATCH-DOC", "msgid": "10", "num_buffers":1}')
    assert partial is None

    partial = await r.consume('{}')
    assert partial is None

    partial = await r.consume('{"bar": 10}')
    assert partial is None

    partial = await r.consume('{"id": "buf_header"}')
    assert partial is None

    partial = await r.consume(b'payload')
    assert partial is not None
    assert partial.msgtype == "PATCH-DOC"
    assert partial.header == {
        "msgtype": "PATCH-DOC",
        "msgid": "10",
        "num_buffers": 1
    }
    assert partial.content == {"bar": 10}
    assert partial.metadata == {}
    assert partial.buffers == [Buffer(ID("buf_header"), b"payload")]
Example #9
0
    def test_server_session_periodic_async(self,
                                           ManagedServerLoop: MSL) -> None:
        application = Application()
        with ManagedServerLoop(application) as server:
            doc = document.Document()
            doc.add_root(DictModel())

            client_session = push_session(
                doc,
                session_id=ID("test_server_session_periodic_async"),
                url=url(server),
                io_loop=server.io_loop)
            server_session = server.get_session('/', client_session.id)

            result = next(iter(server_session.document.roots))

            async def cb():
                # we're testing that we can modify the doc and be
                # "inside" the document lock
                result.values['a'] = 0
                result.values['b'] = await self.async_value(1)
                result.values['c'] = await self.async_value(2)
                result.values['d'] = await self.async_value(3)
                result.values['e'] = await self.async_value(4)
                client_session.close()
                return 5

            cb_id = server_session.document.add_periodic_callback(cb, 10)

            client_session._loop_until_closed()

            server_session.document.remove_periodic_callback(cb_id)

            assert dict(a=0, b=1, c=2, d=3, e=4) == result.values
Example #10
0
    def test_ping(self, ManagedServerLoop: MSL) -> None:
        application = Application()
        with ManagedServerLoop(application,
                               keep_alive_milliseconds=0) as server:
            session = ClientSession(session_id=ID("test_ping"),
                                    websocket_url=ws_url(server),
                                    io_loop=server.io_loop)
            session.connect()
            assert session.connected
            assert session.document is None

            connection = next(iter(server._tornado._clients))
            expected_pong = connection._ping_count
            server._tornado._keep_alive()  # send ping
            session.force_roundtrip()

            assert expected_pong == connection._socket.latest_pong

            # check that each ping increments by 1
            server._tornado._keep_alive()
            session.force_roundtrip()

            assert (expected_pong + 1) == connection._socket.latest_pong

            session.close()
            session._loop_until_closed()
            assert not session.connected
Example #11
0
    def test_client_session_next_tick_async(self,
                                            ManagedServerLoop: MSL) -> None:
        application = Application()
        with ManagedServerLoop(application) as server:
            doc = document.Document()

            client_session = push_session(
                doc,
                session_id=ID("test_client_session_next_tick_async"),
                url=url(server),
                io_loop=server.io_loop)

            result = DictModel()
            doc.add_root(result)

            async def cb():
                result.values['a'] = 0
                result.values['b'] = await self.async_value(1)
                result.values['c'] = await self.async_value(2)
                result.values['d'] = await self.async_value(3)
                result.values['e'] = await self.async_value(4)
                client_session.close()
                return 5

            cb_id = doc.add_next_tick_callback(cb)

            client_session._loop_until_closed()

            with pytest.raises(ValueError) as exc:
                doc.remove_next_tick_callback(cb_id)
            assert 'already removed' in repr(exc.value)

            assert dict(a=0, b=1, c=2, d=3, e=4) == result.values
Example #12
0
def test_server_examples(server_example: Example, example: Example,
                         report: List[Example], config: _pytest.config.Config,
                         bokeh_server: str) -> None:
    if config.option.verbose:
        print()
    app = build_single_handler_application(example.path)
    doc = app.create_document()

    # remove all next-tick, periodic, and timeout callbacks
    for session_callback in doc.session_callbacks:
        if isinstance(session_callback, NextTickCallback):
            doc.remove_next_tick_callback(session_callback)
        elif isinstance(session_callback, PeriodicCallback):
            doc.remove_periodic_callback(session_callback)
        elif isinstance(session_callback, TimeoutCallback):
            doc.remove_timeout_callback(session_callback)
        else:
            raise RuntimeError('Unhandled callback type',
                               type(session_callback))

    session_id = ID(basename(example.path))
    push_session(doc, session_id=session_id)

    if example.no_js:
        if not config.option.no_js:
            warn(f"skipping bokehjs for {example.relpath}")
    else:
        _run_in_browser(
            example, f"http://localhost:5006/?bokeh-session-id={session_id}",
            report, config.option.verbose)
Example #13
0
 def test__check_error_404(self, ManagedServerLoop: MSL) -> None:
     application = Application()
     with ManagedServerLoop(application) as server:
         with pytest.raises(IOError):
             pull_session(session_id=ID("test__check_error_404"),
                          url=url(server) + 'file_not_found',
                          io_loop=server.io_loop)
Example #14
0
    def test_pull_large_document(self, ManagedServerLoop: MSL) -> None:
        application = Application()

        def add_roots(doc):
            import numpy as np
            rows, cols = (40000, 100)
            columns = ['x' + str(i) for i in range(cols)]
            a = np.random.randn(cols, rows)
            source = ColumnDataSource(data=dict(zip(columns, a)))
            doc.add_root(source)

        handler = FunctionHandler(add_roots)
        application.add(handler)

        with ManagedServerLoop(application) as server:
            client_session = pull_session(session_id=ID("test_pull_document"),
                                          url=url(server),
                                          io_loop=server.io_loop,
                                          max_message_size=50000000)
            assert len(client_session.document.roots) == 1

            server_session = server.get_session('/', client_session.id)
            assert len(server_session.document.roots) == 1

            results = {}
            for r in server_session.document.roots:
                if hasattr(r, 'data'):
                    results['data'] = r.data
            assert len(list(results['data'].keys())) == 100
            assert all(len(x) == 40000 for x in results['data'].values())

            client_session.close()
            client_session._loop_until_closed()
            assert not client_session.connected
Example #15
0
    def test_pull_document(self, ManagedServerLoop: MSL) -> None:
        application = Application()

        def add_roots(doc: Document):
            doc.add_root(AnotherModelInTestClientServer(bar=43))
            doc.add_root(
                SomeModelInTestClientServer(foo=42,
                                            data=bytes(
                                                [0x00, 0x01, 0xFE, 0xFF])))

        handler = FunctionHandler(add_roots)
        application.add(handler)

        with ManagedServerLoop(application) as server:
            client_session = pull_session(session_id=ID("test_pull_document"),
                                          url=url(server),
                                          io_loop=server.io_loop)
            assert len(client_session.document.roots) == 2

            server_session = server.get_session('/', client_session.id)
            assert len(server_session.document.roots) == 2

            results = {}
            for r in server_session.document.roots:
                if hasattr(r, 'foo'):
                    results['foo'] = r.foo
                if hasattr(r, 'bar'):
                    results['bar'] = r.bar
            assert results['foo'] == 42
            assert results['bar'] == 43

            client_session.close()
            client_session._loop_until_closed()
            assert not client_session.connected
Example #16
0
def test_server_changes_do_not_boomerang(monkeypatch: pytest.MonkeyPatch,
                                         ManagedServerLoop: MSL) -> None:
    application = Application()
    with ManagedServerLoop(application) as server:
        doc = document.Document()
        client_root = SomeModelInTestClientServer(
            foo=42, data=bytes([0x00, 0x01, 0xFE, 0xFF]))
        doc.add_root(client_root)

        client_session = push_session(
            doc,
            session_id=ID("test_server_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.foo == 42
        assert server_root.foo == 42

        got_angry = {}
        got_angry['result'] = None

        # trap any boomerang
        def get_angry(message, connection):
            got_angry['result'] = message

        monkeypatch.setattr(server_session, '_handle_patch', get_angry)

        # Now modify the server document
        def do_set_foo_property():
            server_root.foo = 57

        server.io_loop.add_callback(server_session.with_document_locked,
                                    do_set_foo_property)

        # 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

        # 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

        client_session.close()
        client_session._loop_until_closed()
        assert not client_session.connected
Example #17
0
    def test_client_changes_go_to_server(self, ManagedServerLoop: MSL) -> None:
        application = Application()
        with ManagedServerLoop(application) as server:
            doc = document.Document()
            client_root = SomeModelInTestClientServer(
                foo=42, data=bytes([0x00, 0x01, 0xFE, 0xFF]))

            client_session = push_session(
                doc,
                session_id=ID("test_client_changes_go_to_server"),
                url=url(server),
                io_loop=server.io_loop)
            server_session = server.get_session('/', client_session.id)

            assert len(server_session.document.roots) == 0

            doc.add_root(client_root)
            client_session.force_roundtrip(
            )  # be sure events have been handled on server

            assert len(server_session.document.roots) == 1
            server_root = next(iter(server_session.document.roots))

            assert client_root.foo == 42
            assert server_root.foo == 42

            # Now try setting title
            assert server_session.document.title == document.DEFAULT_TITLE
            doc.title = "Client Title"
            client_session.force_roundtrip(
            )  # be sure events have been handled on server

            assert server_session.document.title == "Client Title"

            # Now modify an attribute on a client model
            client_root.foo = 57

            # 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 server_change_made():
                return server_root.foo == 57

            client_session._connection._loop_until(server_change_made)
            assert server_root.foo == 57

            doc.remove_root(client_root)
            client_session.force_roundtrip(
            )  # be sure events have been handled on server
            assert len(server_session.document.roots) == 0

            client_session.close()
            client_session._loop_until_closed()
            assert not client_session.connected
Example #18
0
    def test_version(self, monkeypatch: pytest.MonkeyPatch,
                     test_plot: figure) -> None:
        from bokeh import __version__

        out = bes.json_item(test_plot, target=ID("foo"))
        assert set(out.keys()) == JSON_ITEMS_KEYS
        assert out['doc']['version'] == __version__

        out = bes.json_item(test_plot)
        assert set(out.keys()) == JSON_ITEMS_KEYS
        assert out['doc']['version'] == __version__
Example #19
0
 def test_minimal_connect_and_disconnect(self,
                                         ManagedServerLoop: MSL) -> None:
     application = Application()
     with ManagedServerLoop(application) as server:
         # we don't have to start the server because it
         # uses the same main loop as the client, so
         # if we start either one it starts both
         session = ClientSession(
             session_id=ID("test_minimal_connect_and_disconnect"),
             io_loop=server.io_loop,
             websocket_url=ws_url(server))
         session.connect()
         assert session.connected
Example #20
0
def test_client_changes_do_not_boomerang(monkeypatch: pytest.MonkeyPatch,
                                         ManagedServerLoop: MSL) -> None:
    application = Application()
    with ManagedServerLoop(application) as server:
        doc = document.Document()
        client_root = SomeModelInTestClientServer(
            foo=42, data=bytes([0x00, 0x01, 0xFE, 0xFF]))
        doc.add_root(client_root)

        client_session = push_session(
            doc,
            session_id=ID("test_client_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.foo == 42
        assert server_root.foo == 42

        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)

        # Now modify the client document
        client_root.foo = 57

        # wait until server side change made ... but we may not have the
        # boomerang yet
        def server_change_made():
            return server_root.foo == 57

        client_session._connection._loop_until(server_change_made)
        assert server_root.foo == 57

        # 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

        client_session.close()
        client_session._loop_until_closed()
        assert not client_session.connected
        server.unlisten()  # clean up so next test can run
Example #21
0
def test__lifecycle_hooks(ManagedServerLoop: MSL) -> None:
    application = Application()
    handler = HookTestHandler()
    application.add(handler)
    with ManagedServerLoop(application,
                           check_unused_sessions_milliseconds=30) as server:
        client_session = pull_session(session_id=ID("test__lifecycle_hooks"),
                                      url=url(server),
                                      io_loop=server.io_loop)
        client_doc = client_session.document
        assert len(client_doc.roots) == 1

        server_session = server.get_session('/', client_session.id)
        server_doc = server_session.document
        assert len(server_doc.roots) == 1

        # save for later, since doc.roots will be emptied after the session is closed
        client_hook_list = list(client_doc.roots[0].hooks)
        server_hook_list = list(server_doc.roots[0].hooks)

        client_session.close()

        # expire the session quickly rather than after the usual timeout
        server_session.request_expiration()

        server.io_loop.call_later(0.1, lambda: server.io_loop.stop())

        server.io_loop.start()

    assert handler.hooks == [
        "server_loaded",
        "session_created",
        "modify",
        "next_tick",
        "timeout",
        "periodic",
        "session_destroyed",
        "server_unloaded",
    ]

    assert handler.load_count == 1
    assert handler.unload_count == 1

    # 3 instead of 6, because locked callbacks on destroyed sessions become no-ops
    assert handler.session_creation_async_value == 3
    assert client_doc.title == "Modified"
    assert server_doc.title == "Modified"

    # only the handler sees "session_destroyed" since the session is shut down at that point.
    assert client_hook_list == ["session_created", "modify"]
    assert server_hook_list == ["session_created", "modify"]
Example #22
0
 def test_disconnect_on_error(self, ManagedServerLoop: MSL) -> None:
     application = Application()
     with ManagedServerLoop(application) as server:
         session = ClientSession(session_id=ID("test_disconnect_on_error"),
                                 websocket_url=ws_url(server),
                                 io_loop=server.io_loop)
         session.connect()
         assert session.connected
         # send a bogus message using private fields
         server.io_loop.add_callback(
             session._connection._socket.write_message, b"xx", binary=True)
         # connection should now close on the server side
         # and the client loop should end
         session._loop_until_closed()
         assert not session.connected
         session.close()
         session._loop_until_closed()
         assert not session.connected
Example #23
0
    def test_request_server_info(self, ManagedServerLoop: MSL) -> None:
        application = Application()
        with ManagedServerLoop(application) as server:
            session = ClientSession(session_id=ID("test_request_server_info"),
                                    websocket_url=ws_url(server),
                                    io_loop=server.io_loop)
            session.connect()
            assert session.connected
            assert session.document is None

            info = session.request_server_info()

            from bokeh import __version__

            assert info['version_info']['bokeh'] == __version__
            assert info['version_info']['server'] == __version__

            session.close()
            session._loop_until_closed()
            assert not session.connected
Example #24
0
def stable_id() -> ID:
    return ID('ID')
Example #25
0
 def test_root_id(self, test_plot: figure) -> None:
     out = bes.json_item(test_plot, target=ID("foo"))
     assert set(out.keys()) == JSON_ITEMS_KEYS
     assert out['doc']['roots'][0]["id"] == out['root_id']
Example #26
0
 def test_doc_title(self, test_plot: figure) -> None:
     out = bes.json_item(test_plot, target=ID("foo"))
     assert set(out.keys()) == JSON_ITEMS_KEYS
     assert out['doc']['title'] == ""
Example #27
0
 def test_doc_json(self, test_plot: figure) -> None:
     out = bes.json_item(test_plot, target=ID("foo"))
     assert set(out.keys()) == JSON_ITEMS_KEYS
     expected = list(standalone_docs_json([test_plot]).values())[0]
     assert out['doc'] == expected
Example #28
0
 def test_with_target_id(self, test_plot: figure) -> None:
     out = bes.json_item(test_plot, target=ID("foo"))
     assert set(out.keys()) == JSON_ITEMS_KEYS
     assert out['target_id'] == "foo"
Example #29
0
    def test_server_changes_go_to_client(self, ManagedServerLoop: MSL) -> None:
        application = Application()
        with ManagedServerLoop(application) as server:
            doc = document.Document()

            client_session = push_session(
                doc,
                session_id=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, data=bytes([0x00, 0x01, 0xFE, 0xFF]))

            def do_add_server_root():
                server_session.document.add_root(server_root)

            server.io_loop.add_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.add_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.add_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.add_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
Example #30
0
    def test_lots_of_concurrent_messages(self, ManagedServerLoop: MSL) -> None:
        application = Application()

        def setup_stuff(doc):
            m1 = AnotherModelInTestClientServer(bar=43, name='m1')
            m2 = SomeModelInTestClientServer(foo=42,
                                             name='m2',
                                             data=bytes(
                                                 [0x00, 0x01, 0xFE, 0xFF]))
            m3 = SomeModelInTestClientServer(foo=68,
                                             name='m3',
                                             data=bytes(
                                                 [0x00, 0x01, 0xFE, 0xFF]))
            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=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