def test_simple(self): graph = { "l": {"o"}, "m": {"n", "o"}, "n": {"o"}, "o": set(), "p": {"o"} } res = list(lexicographical_topological_sort(graph, key=lambda x: x)) self.assertEqual(["o", "l", "n", "m", "p"], res)
def test_simple(self): graph = { "l": {"o"}, "m": {"n", "o"}, "n": {"o"}, "o": set(), "p": {"o"}, } res = list(lexicographical_topological_sort(graph, key=lambda x: x)) self.assertEqual(["o", "l", "n", "m", "p"], res)
def test_simple(self) -> None: graph: Dict[str, Set[str]] = { "l": {"o"}, "m": {"n", "o"}, "n": {"o"}, "o": set(), "p": {"o"}, } res = list(lexicographical_topological_sort(graph, key=lambda x: x)) self.assertEqual(["o", "l", "n", "m", "p"], res)
def do_check(self, events, edges, expected_state_ids): """Take a list of events and edges and calculate the state of the graph at END, and asserts it matches `expected_state_ids` Args: events (list[FakeEvent]) edges (list[list[str]]): A list of chains of event edges, e.g. `[[A, B, C]]` are edges A->B and B->C. expected_state_ids (list[str]): The expected state at END, (excluding the keys that haven't changed since START). """ # We want to sort the events into topological order for processing. graph = {} # node_id -> FakeEvent fake_event_map = {} for ev in itertools.chain(INITIAL_EVENTS, events): graph[ev.node_id] = set() fake_event_map[ev.node_id] = ev for a, b in pairwise(INITIAL_EDGES): graph[a].add(b) for edge_list in edges: for a, b in pairwise(edge_list): graph[a].add(b) # event_id -> FrozenEvent event_map = {} # node_id -> state state_at_event = {} # We copy the map as the sort consumes the graph graph_copy = {k: set(v) for k, v in graph.items()} for node_id in lexicographical_topological_sort(graph_copy, key=lambda e: e): fake_event = fake_event_map[node_id] event_id = fake_event.event_id prev_events = list(graph[node_id]) if len(prev_events) == 0: state_before = {} elif len(prev_events) == 1: state_before = dict(state_at_event[prev_events[0]]) else: state_d = resolve_events_with_store( FakeClock(), ROOM_ID, RoomVersions.V2.identifier, [state_at_event[n] for n in prev_events], event_map=event_map, state_res_store=TestStateResolutionStore(event_map), ) state_before = self.successResultOf( defer.ensureDeferred(state_d)) state_after = dict(state_before) if fake_event.state_key is not None: state_after[(fake_event.type, fake_event.state_key)] = event_id auth_types = set(auth_types_for_event(fake_event)) auth_events = [] for key in auth_types: if key in state_before: auth_events.append(state_before[key]) event = fake_event.to_event(auth_events, prev_events) state_at_event[node_id] = state_after event_map[event_id] = event expected_state = {} for node_id in expected_state_ids: # expected_state_ids are node IDs rather than event IDs, # so we have to convert event_id = EventID(node_id, "example.com").to_string() event = event_map[event_id] key = (event.type, event.state_key) expected_state[key] = event_id start_state = state_at_event["START"] end_state = { key: value for key, value in state_at_event["END"].items() if key in expected_state or start_state.get(key) != value } self.assertEqual(expected_state, end_state)
def do_check(self, events, edges, expected_state_ids): """Take a list of events and edges and calculate the state of the graph at END, and asserts it matches `expected_state_ids` Args: events (list[FakeEvent]) edges (list[list[str]]): A list of chains of event edges, e.g. `[[A, B, C]]` are edges A->B and B->C. expected_state_ids (list[str]): The expected state at END, (excluding the keys that haven't changed since START). """ # We want to sort the events into topological order for processing. graph = {} # node_id -> FakeEvent fake_event_map = {} for ev in itertools.chain(INITIAL_EVENTS, events): graph[ev.node_id] = set() fake_event_map[ev.node_id] = ev for a, b in pairwise(INITIAL_EDGES): graph[a].add(b) for edge_list in edges: for a, b in pairwise(edge_list): graph[a].add(b) # event_id -> FrozenEvent event_map = {} # node_id -> state state_at_event = {} # We copy the map as the sort consumes the graph graph_copy = {k: set(v) for k, v in graph.items()} for node_id in lexicographical_topological_sort(graph_copy, key=lambda e: e): fake_event = fake_event_map[node_id] event_id = fake_event.event_id prev_events = list(graph[node_id]) if len(prev_events) == 0: state_before = {} elif len(prev_events) == 1: state_before = dict(state_at_event[prev_events[0]]) else: state_d = resolve_events_with_store( [state_at_event[n] for n in prev_events], event_map=event_map, state_res_store=TestStateResolutionStore(event_map), ) state_before = self.successResultOf(state_d) state_after = dict(state_before) if fake_event.state_key is not None: state_after[(fake_event.type, fake_event.state_key)] = event_id auth_types = set(auth_types_for_event(fake_event)) auth_events = [] for key in auth_types: if key in state_before: auth_events.append(state_before[key]) event = fake_event.to_event(auth_events, prev_events) state_at_event[node_id] = state_after event_map[event_id] = event expected_state = {} for node_id in expected_state_ids: # expected_state_ids are node IDs rather than event IDs, # so we have to convert event_id = EventID(node_id, "example.com").to_string() event = event_map[event_id] key = (event.type, event.state_key) expected_state[key] = event_id start_state = state_at_event["START"] end_state = { key: value for key, value in state_at_event["END"].items() if key in expected_state or start_state.get(key) != value } self.assertEqual(expected_state, end_state)
def do_check( self, events: List[FakeEvent], edges: List[List[str]], expected_state_ids: List[str], ) -> None: """Take a list of events and edges and calculate the state of the graph at END, and asserts it matches `expected_state_ids` Args: events edges: A list of chains of event edges, e.g. `[[A, B, C]]` are edges A->B and B->C. expected_state_ids: The expected state at END, (excluding the keys that haven't changed since START). """ # We want to sort the events into topological order for processing. graph: Dict[str, Set[str]] = {} fake_event_map: Dict[str, FakeEvent] = {} for ev in itertools.chain(INITIAL_EVENTS, events): graph[ev.node_id] = set() fake_event_map[ev.node_id] = ev for a, b in pairwise(INITIAL_EDGES): graph[a].add(b) for edge_list in edges: for a, b in pairwise(edge_list): graph[a].add(b) event_map: Dict[str, EventBase] = {} state_at_event: Dict[str, StateMap[str]] = {} # We copy the map as the sort consumes the graph graph_copy = {k: set(v) for k, v in graph.items()} for node_id in lexicographical_topological_sort(graph_copy, key=lambda e: e): fake_event = fake_event_map[node_id] event_id = fake_event.event_id prev_events = list(graph[node_id]) state_before: StateMap[str] if len(prev_events) == 0: state_before = {} elif len(prev_events) == 1: state_before = dict(state_at_event[prev_events[0]]) else: state_d = resolve_events_with_store( FakeClock(), ROOM_ID, RoomVersions.V2, [state_at_event[n] for n in prev_events], event_map=event_map, state_res_store=TestStateResolutionStore(event_map), ) state_before = self.successResultOf( defer.ensureDeferred(state_d)) state_after = dict(state_before) if fake_event.state_key is not None: state_after[(fake_event.type, fake_event.state_key)] = event_id # This type ignore is a bit sad. Things we have tried: # 1. Define a `GenericEvent` Protocol satisfied by FakeEvent, EventBase and # EventBuilder. But this is Hard because the relevant attributes are # DictProperty[T] descriptors on EventBase but normal Ts on FakeEvent. # 2. Define a `GenericEvent` Protocol describing `FakeEvent` only, and # change this function to accept Union[Event, EventBase, EventBuilder]. # This seems reasonable to me, but mypy isn't happy. I think that's # a mypy bug, see https://github.com/python/mypy/issues/5570 # Instead, resort to a type-ignore. auth_types = set(auth_types_for_event( RoomVersions.V6, fake_event)) # type: ignore[arg-type] auth_events = [] for key in auth_types: if key in state_before: auth_events.append(state_before[key]) event = fake_event.to_event(auth_events, prev_events) state_at_event[node_id] = state_after event_map[event_id] = event expected_state = {} for node_id in expected_state_ids: # expected_state_ids are node IDs rather than event IDs, # so we have to convert event_id = EventID(node_id, "example.com").to_string() event = event_map[event_id] key = (event.type, event.state_key) expected_state[key] = event_id start_state = state_at_event["START"] end_state = { key: value for key, value in state_at_event["END"].items() if key in expected_state or start_state.get(key) != value } self.assertEqual(expected_state, end_state)