def test_manager_on_update_rows(self, sentinel): s = sentinel(0) def update_callback(port_id, delta): table = Table(delta) assert table.size() == 1 assert table.schema() == { "a": int, "b": str } table.delete() s.set(s.get() + 1) # create a table and view using manager make_table = {"id": 1, "name": "table1", "cmd": "table", "args": [data]} manager = PerspectiveManager() manager._process(make_table, self.post) make_view = {"id": 2, "table_name": "table1", "view_name": "view1", "cmd": "view"} manager._process(make_view, self.post) # hook into the created view and pass it the callback view = manager._views["view1"] view.on_update(update_callback, mode="row") # call updates update1 = {"id": 3, "name": "table1", "cmd": "table_method", "method": "update", "args": [{"a": [4], "b": ["d"]}]} update2 = {"id": 4, "name": "table1", "cmd": "table_method", "method": "update", "args": [{"a": [5], "b": ["e"]}]} manager._process(update1, self.post) manager._process(update2, self.post) assert s.get() == 2
def test_manager_table_validate_expressions(self): post_callback = partial(self.validate_post, expected={ "id": 1, "data": { "expression_schema": { "abc": "float" }, "errors": {} } }) message = { "id": 1, "name": "table1", "cmd": "table_method", "method": "validate_expressions", "args": [['// abc \n "a" + "a"']] } manager = PerspectiveManager() table = Table(data) manager.host_table("table1", table) manager._process(message, post_callback)
def test_manager_view_schema(self): post_callback = partial(self.validate_post, expected={ "id": 1, "data": { "a": "integer", "b": "integer" } }) message = { "id": 1, "name": "view1", "cmd": "view_method", "method": "schema", "args": [False] } manager = PerspectiveManager() table = Table(data) view = table.view(row_pivots=["a"]) manager.host_table("table1", table) manager.host_view("view1", view) manager._process(message, post_callback)
def test_manager_delete_view(self): make_table = {"id": 1, "name": "table1", "cmd": "table", "args": [data]} manager = PerspectiveManager() manager._process(make_table, self.post) make_view = {"id": 2, "table_name": "table1", "view_name": "view1", "cmd": "view"} manager._process(make_view, self.post) delete_view = {"id": 3, "name": "view1", "cmd": "view_method", "method": "delete"} manager._process(delete_view, self.post) assert len(manager._views) == 0
def test_manager_clear_view_no_client_id(self): messages = [{ "id": 1, "table_name": "table1", "view_name": "view1", "cmd": "view" }, { "id": 2, "table_name": "table1", "view_name": "view2", "cmd": "view" }, { "id": 3, "table_name": "table1", "view_name": "view3", "cmd": "view" }] manager = PerspectiveManager() table = Table(data) manager.host_table("table1", table) for message in messages: manager._process(message, self.post) with raises(PerspectiveError): manager.clear_views(None)
def test_manager_clear_view(self): messages = [{ "id": 1, "table_name": "table1", "view_name": "view1", "cmd": "view" }, { "id": 2, "table_name": "table1", "view_name": "view2", "cmd": "view" }, { "id": 3, "table_name": "table1", "view_name": "view3", "cmd": "view" }] manager = PerspectiveManager() table = Table(data) manager.host_table("table1", table) for message in messages: manager._process(message, self.post, client_id=1) manager.clear_views(1) assert manager._views == {}
def test_manager_create_view_two(self): message = { "id": 1, "table_name": "table1", "view_name": "view1", "cmd": "view", "config": { "row_pivots": ["a"], "column_pivots": ["b"] } } manager = PerspectiveManager() table = Table(data) manager.host_table("table1", table) manager._process(message, self.post) assert manager._views["view1"].to_dict() == { "__ROW_PATH__": [[], ["1"], ["2"], ["3"]], "a|a": [1, 1, None, None], "a|b": [1, 1, None, None], "b|a": [2, None, 2, None], "b|b": [1, None, 1, None], "c|a": [3, None, None, 3], "c|b": [1, None, None, 1] }
def test_manager_remove_update(self, sentinel): s = sentinel(0) def update_callback(): s.set(s.get() + 1) # create a table and view using manager make_table = { "id": 1, "name": "table1", "cmd": "table", "args": [data] } manager = PerspectiveManager() manager._process(make_table, self.post) make_view = { "id": 2, "table_name": "table1", "view_name": "view1", "cmd": "view" } manager._process(make_view, self.post) # hook into the created view and pass it the callback view = manager._views["view1"] view.on_update(update_callback) view.remove_update(update_callback) # call updates update1 = { "id": 4, "name": "table1", "cmd": "table_method", "method": "update", "args": [{ "a": [4], "b": ["d"] }] } update2 = { "id": 5, "name": "table1", "cmd": "table_method", "method": "update", "args": [{ "a": [5], "b": ["e"] }] } manager._process(update1, self.post) manager._process(update2, self.post) assert s.get() == 0
def test_arbitary_lock_unlock_manager(self): manager = PerspectiveManager(lock=True) make_table_message = { "id": 1, "name": "table1", "cmd": "table", "args": [data] } manager._process( make_table_message, partial(self.validate_post, expected={ "id": 1, "error": "`table` failed - access denied" })) manager.unlock() manager._process(make_table_message, self.post) assert manager._tables["table1"].schema() == {"a": int, "b": str} update_message = { "id": 2, "name": "table1", "cmd": "table_method", "method": "update", "args": [data] } manager._process(update_message, self.post) assert manager._tables["table1"].size() == 6 manager.lock() update_message_new = { "id": 3, "name": "table1", "cmd": "table_method", "method": "update", "args": [data] } manager._process( update_message_new, partial(self.validate_post, expected={ "id": 3, "error": "`table_method.update` failed - access denied" }))
def test_manager_on_update_through_wire_API(self, sentinel): s = sentinel(0) # create a table and view using manager make_table = {"id": 1, "name": "table1", "cmd": "table", "args": [data]} manager = PerspectiveManager() manager._process(make_table, self.post) make_view = {"id": 2, "table_name": "table1", "view_name": "view1", "cmd": "view"} manager._process(make_view, self.post) def callback(updated): assert updated["port_id"] == 0 s.set(s.get() + 100) # simulate a client that holds callbacks by id callbacks = { 3: callback } def post_update(msg): # when `on_update` is triggered, this callback gets the message # and has to decide which callback to trigger. message = json.loads(msg) assert message["id"] is not None if message["id"] == 3: # trigger callback assert message["data"] == { "port_id": 0 } callbacks[message["id"]](message["data"]) # hook into the created view and pass it the callback make_on_update = {"id": 3, "name": "view1", "cmd": "view_method", "subscribe": True, "method": "on_update", "callback_id": "callback_1"} manager._process(make_on_update, post_update) # call updates update1 = {"id": 4, "name": "table1", "cmd": "table_method", "method": "update", "args": [{"a": [4], "b": ["d"]}]} update2 = {"id": 5, "name": "table1", "cmd": "table_method", "method": "update", "args": [{"a": [5], "b": ["e"]}]} manager._process(update1, self.post) manager._process(update2, self.post) assert s.get() == 200
def test_manager_on_delete_view(self, sentinel): s = sentinel(False) def delete_callback(): s.set(True) make_table = {"id": 1, "name": "table1", "cmd": "table", "args": [data]} manager = PerspectiveManager() manager._process(make_table, self.post) make_view = {"id": 2, "table_name": "table1", "view_name": "view1", "cmd": "view"} manager._process(make_view, self.post) view = manager._get_view("view1") view.on_delete(delete_callback) delete_view = {"id": 3, "name": "view1", "cmd": "view_method", "method": "delete"} manager._process(delete_view, self.post) assert len(manager._views) == 0 assert s.get() is True
def test_session_close_multiple_sessions_with_callbacks(self, sentinel): s = sentinel(0) manager = PerspectiveManager() sessions = [manager.new_session() for i in range(5)] # create a table and view using manager make_table = { "id": 1, "name": "table1", "cmd": "table", "args": [data] } manager._process(make_table, self.post) # create a view on each session for i, session in enumerate(sessions): # IDs have to conflict - each viewer will send the first message as # ID = 1, so we need to make sure we handle that. msg = { "id": 2, "table_name": "table1", "view_name": "view" + str(i), "cmd": "view" } session.process(msg, self.post) manager_views = list(manager._views.keys()) for key in ["view" + str(i) for i in range(5)]: assert key in manager_views for i, session in enumerate(sessions): view = manager._get_view("view" + str(i)) assert view._client_id == session.client_id def callback(updated): assert updated["port_id"] == 0 s.set(s.get() + 100) # simulate a client that holds callbacks by id callbacks = {3: callback} def post_update(msg): # when `on_update` is triggered, this callback gets the message # and has to decide which callback to trigger. message = json.loads(msg) assert message["id"] is not None if message["id"] == 3: # trigger callback assert message["data"] == {"port_id": 0} callbacks[message["id"]](message["data"]) # create a view and an on_update on each session for i, session in enumerate(sessions): view_name = "view" + str(i) # IDs have to conflict - each viewer will send the first message as # ID = 1, so we need to make sure we handle that. msg = { "id": 2, "table_name": "table1", "view_name": view_name, "cmd": "view" } session.process(msg, self.post) make_on_update = { "id": 3, "name": view_name, "cmd": "view_method", "subscribe": True, "method": "on_update", "callback_id": "callback_1" } session.process(make_on_update, post_update) # call updates using a random session - they should propagate random_session_id = random.randint(0, 4) random_session = sessions[random_session_id] random_client_id = random_session.client_id update1 = { "id": 4, "name": "table1", "cmd": "table_method", "method": "update", "args": [{ "a": [4], "b": ["d"] }] } update2 = { "id": 5, "name": "table1", "cmd": "table_method", "method": "update", "args": [{ "a": [5], "b": ["e"] }] } random_session.process(update1, self.post) random_session.process(update2, self.post) # all updates processed, all callbacks fired assert s.get() == 1000 # close a random session, and make sure the other views and callbacks # are not affected random_session.close() # make sure the view is gone - but not the table assert "table1" in manager._tables assert "view" + str(random_session_id) not in manager._views.keys() assert len(manager._views.keys()) == 4 for callback in manager._callback_cache: assert callback["client_id"] != random_client_id assert len(manager._callback_cache) == 4
def test_manager_on_update_rows_with_port_id_through_wire_API(self, sentinel): s = sentinel(0) def update_callback(port_id, delta): table = Table(delta) assert table.size() == 1 assert table.schema() == { "a": int, "b": str } table.delete() s.set(s.get() + 1) # create a table and view using manager make_table = {"id": 1, "name": "table1", "cmd": "table", "args": [data]} manager = PerspectiveManager() manager._process(make_table, self.post) make_view = {"id": 2, "table_name": "table1", "view_name": "view1", "cmd": "view"} manager._process(make_view, self.post) # Get two ports on the table make_port = {"id": 3, "name": "table1", "cmd": "table_method", "method": "make_port"} make_port2 = {"id": 4, "name": "table1", "cmd": "table_method", "method": "make_port"} manager._process(make_port, self.post) manager._process(make_port2, self.post) # define an update callback def callback(delta): table = Table(delta) assert table.size() == 1 assert table.schema() == { "a": int, "b": str } table.delete() s.set(s.get() + 100) # simulate a client that holds callbacks by id callbacks = { 3: callback } def post_update(msg, binary=False): # when `on_update` is triggered, this callback gets the message # and has to decide which callback to trigger. if binary: # trigger callback - "msg" here is binary callbacks[3](msg) return message = json.loads(msg) assert message["id"] is not None if message["id"] == 3: # wait for transferable assert message["data"]["port_id"] in (1, 2) return # hook into the created view and pass it the callback make_on_update = { "id": 5, "name": "view1", "cmd": "view_method", "subscribe": True, "method": "on_update", "callback_id": "callback_1", "args": [{"mode": "row"}] } manager._process(make_on_update, post_update) # call updates update1 = {"id": 6, "name": "table1", "cmd": "table_method", "method": "update", "args": [{"a": [4], "b": ["d"]}, {"port_id": 1}]} update2 = {"id": 7, "name": "table1", "cmd": "table_method", "method": "update", "args": [{"a": [5], "b": ["e"]}, {"port_id": 2}]} manager._process(update1, self.post) manager._process(update2, self.post) assert s.get() == 200
def test_manager_create_table(self): message = {"id": 1, "name": "table1", "cmd": "table", "args": [data]} manager = PerspectiveManager() manager._process(message, self.post) assert manager._tables["table1"].schema() == {"a": int, "b": str}