def test_non_switchable_goals_disappear_on_selection_change(): e = SwitchableView(Goals("root")) e.accept_all(Add("1"), Add("2"), Select(2), ToggleSwitchableView(), Select(2)) assert e.q("name,switchable,select") == { 1: { "name": "root", "switchable": False, "select": "prev" }, 2: { "name": "1", "switchable": True, "select": "select" }, 3: { "name": "2", "switchable": True, "select": None }, } e.accept(HoldSelect()) assert e.q("name,switchable,select") == { 2: { "name": "1", "switchable": True, "select": "select" }, 3: { "name": "2", "switchable": True, "select": None }, }
def test_new_goal_is_added_to_the_selected_node(self): self.goals.accept_all(Add("A"), Select(2)) assert self.goals.q(keys="name,select") == { 1: { "name": "Root", "select": "prev" }, 2: { "name": "A", "select": "select" }, } self.goals.accept(Add("B")) assert self.goals.q(keys="name,select,edge") == { 1: { "name": "Root", "select": "prev", "edge": [(2, EdgeType.PARENT)] }, 2: { "name": "A", "select": "select", "edge": [(3, EdgeType.PARENT)] }, 3: { "name": "B", "select": None, "edge": [] }, }
def test_two_new_goals_move_to_top(self): self.goals.accept_all(Add("A"), Add("B")) assert self.goals.q(keys="name,switchable") == { 1: { "name": "Root", "switchable": False }, 2: { "name": "A", "switchable": True }, 3: { "name": "B", "switchable": True }, }
def test_do_not_make_a_link_on_not_old_matching_add(tree_2_goals): goals = tree_2_goals goals.accept_all(ToggleAutoLink("old"), ToggleAutoLink("new")) # Add a goal to the root goals.accept_all(Select(1), Add("This is old subgoal")) assert goals.q("name,edge") == { 1: { "name": "Root", "edge": [(-12, EdgeType.PARENT), (3, EdgeType.PARENT)] }, -12: { "name": "Autolink: 'new'", "edge": [(2, EdgeType.PARENT)] }, 2: { "name": "Autolink on me", "edge": [] }, 3: { "name": "This is old subgoal", "edge": [] }, } assert _autolink_events(goals) == [ ("add_autolink", 2, "old"), ("remove_autolink", 2), ("add_autolink", 2, "new"), ]
def test_make_a_link_on_matching_add(tree_2_goals): goals = tree_2_goals goals.accept(ToggleAutoLink("me")) # Add a goal to the root goals.accept_all(Select(1), Add("Link ME please")) assert goals.q("name,edge") == { 1: { "name": "Root", "edge": [(-12, EdgeType.PARENT), (3, EdgeType.PARENT)] }, -12: { "name": "Autolink: 'me'", "edge": [(2, EdgeType.PARENT)] }, 2: { "name": "Autolink on me", "edge": [(3, EdgeType.BLOCKER)] }, 3: { "name": "Link ME please", "edge": [] }, } assert _autolink_events(goals) == [ ("add_autolink", 2, "me"), ]
def build_actions(command): simple_commands = { "c": ToggleClose(), "d": Delete(), "h": HoldSelect(), "k": ToggleLink(edge_type=EdgeType.PARENT), "l": ToggleLink(), "n": ToggleOpenView(), "p": ToggleProgress(), "t": ToggleSwitchableView(), "z": ToggleZoom(), } if command and all(c in "1234567890" for c in command): return [Select(int(c)) for c in command] if command.startswith("a "): return [Add(command[2:])] if command.startswith("i "): return [Insert(command[2:])] if command.startswith("r "): return [Rename(command[2:])] if command.startswith("f"): return [FilterBy(command[1:].lstrip())] if command.startswith("` "): return [ToggleAutoLink(command[2:])] if command in simple_commands: return [simple_commands[command]] return []
def test_how_should_we_deal_with_zooming(): g = build_goaltree( open_(1, "Root", [2]), open_(2, "Zoomed", blockers=[3], select=selected), open_(3, "Ex-top"), ) v = SwitchableView(persistent_layers(g)) v.accept_all(ToggleZoom(), ToggleSwitchableView()) assert v.q("name,select") == { 2: { "name": "Zoomed", "select": "select" }, 3: { "name": "Ex-top", "select": None }, } v.accept(Add("Unexpectedly hidden")) assert v.q("name,select") == { 2: { "name": "Zoomed", "select": "select" }, 3: { "name": "Ex-top", "select": None }, 4: { "name": "Unexpectedly hidden", "select": None }, }
def test_two_goals_in_a_chain(self): self.goals.accept_all(Add("A"), Add("AA", 2)) assert self.goals.q(keys="name,switchable") == { 1: { "name": "Root", "switchable": False }, 2: { "name": "A", "switchable": False }, 3: { "name": "AA", "switchable": True }, }
def test_insert_goal_in_the_middle(self): self.goals.accept_all(Add("B"), HoldSelect(), Select(2)) assert self.goals.q(keys="name,edge,switchable") == { 1: { "name": "Root", "edge": [(2, EdgeType.PARENT)], "switchable": False }, 2: { "name": "B", "edge": [], "switchable": True }, } self.goals.accept(Insert("A")) assert self.goals.q(keys="name,edge,switchable") == { 1: { "name": "Root", "edge": [(3, EdgeType.PARENT)], "switchable": False }, 2: { "name": "B", "edge": [], "switchable": True }, 3: { "name": "A", "edge": [(2, EdgeType.PARENT)], "switchable": False }, }
def test_autolink_on_all_matching_goals(tree_3v_goals): goals = tree_3v_goals # make 2 autolinks goals.accept_all(ToggleAutoLink("me"), Select(3), ToggleAutoLink("plea")) assert goals.q("name,edge") == { 1: { "name": "Root", "edge": [(-12, EdgeType.PARENT), (-13, EdgeType.PARENT)] }, -12: { "name": "Autolink: 'me'", "edge": [(2, EdgeType.PARENT)] }, -13: { "name": "Autolink: 'plea'", "edge": [(3, EdgeType.PARENT)] }, 2: { "name": "Autolink on me", "edge": [] }, 3: { "name": "Another subgoal", "edge": [] }, } # add 2-mathing goal goals.accept_all(Select(1), Add("Link me to both please")) assert goals.q("name,edge") == { 1: { "name": "Root", "edge": [ (-12, EdgeType.PARENT), (-13, EdgeType.PARENT), (4, EdgeType.PARENT), ], }, -12: { "name": "Autolink: 'me'", "edge": [(2, EdgeType.PARENT)] }, -13: { "name": "Autolink: 'plea'", "edge": [(3, EdgeType.PARENT)] }, 2: { "name": "Autolink on me", "edge": [(4, EdgeType.BLOCKER)] }, 3: { "name": "Another subgoal", "edge": [(4, EdgeType.BLOCKER)] }, 4: { "name": "Link me to both please", "edge": [] }, }
def test_multiple_saves_works_fine(): file_name = NamedTemporaryFile().name goals = all_layers(Goals("Root")) save(goals, file_name) goals.accept(Add("Next")) save(goals, file_name) new_goals = load(file_name) assert goals.q() == new_goals.q()
def test_new_goal_moves_to_top(self): self.goals.accept(Add("A")) assert self.goals.q(keys="name,switchable") == { 1: { "name": "Root", "switchable": False }, 2: { "name": "A", "switchable": True }, }
def test_added_goal_has_strong_link_with_parent(self): self.goals.accept(Add("New")) assert self.goals.q(keys="name,edge") == { 1: { "name": "Root", "edge": [(2, EdgeType.PARENT)] }, 2: { "name": "New", "edge": [] }, }
def test_save_and_load(): file_name = NamedTemporaryFile().name goals = Enumeration(all_layers(Goals("Root"))) goals.accept_all( Add("Top"), Add("Middle"), Select(3), HoldSelect(), Select(2), ToggleLink(), Add("Closed"), Select(4), ToggleClose(), Select(2), ToggleZoom(), ) save(goals, file_name) new_goals = load(file_name) goals.accept_all(ToggleOpenView()) new_goals.accept_all(ToggleOpenView()) assert goals.q(keys="open,name,edge,select,switchable") == new_goals.q( keys="open,name,edge,select,switchable")
def test_root_goal_is_selected_by_default(self): assert self.goals.q(keys="select") == {1: {"select": "select"}} self.goals.accept(Add("A")) assert self.goals.q(keys="select") == { 1: { "select": "select" }, 2: { "select": None }, } self.goals.accept(Add("B")) assert self.goals.q(keys="select") == { 1: { "select": "select" }, 2: { "select": None }, 3: { "select": None }, }
def test_restore_goals_from_db(): file_name = NamedTemporaryFile().name with sqlite3.connect(file_name) as conn: run_migrations(conn) setup_sample_db(conn) actual_goals = load(file_name) actual_goals.accept(ToggleOpenView()) expected_goals = Goals("Root") expected_goals.accept_all( Add("A"), Add("B"), Select(2), HoldSelect(), Select(3), ToggleLink(), Select(3), ToggleClose(), Select(1), HoldSelect(), Select(2), ) keys = "name,edge,open,select" assert expected_goals.q(keys=keys) == actual_goals.q(keys=keys) assert not actual_goals.events()
def test_selection_cache_should_be_reset_after_view_switch(goal_chain_11): e = Enumeration(SwitchableView(goal_chain_11)) e.accept_all(Add("Also top")) e.accept(Select(1)) # Select(1) is kept in a cache and not applied yet e.accept(ToggleSwitchableView()) assert e.q("name,select") == { 1: {"name": "a", "select": "select"}, 2: {"name": "k", "select": None}, 3: {"name": "Also top", "select": None}, } # Select(2) is being applied without any effect from the previous selection # This happens because selection cache was reset e.accept(Select(2)) assert e.q("name,select") == { 1: {"name": "a", "select": "prev"}, 2: {"name": "k", "select": "select"}, 3: {"name": "Also top", "select": None}, }
def test_do_not_make_a_link_on_matching_subgoal_add(tree_2_goals): goals = tree_2_goals goals.accept(ToggleAutoLink("me")) # Add a sub goal to the same subgoal goals.accept_all(Add("Do NOT link me please")) assert goals.q("name,edge") == { 1: { "name": "Root", "edge": [(-12, EdgeType.PARENT)] }, -12: { "name": "Autolink: 'me'", "edge": [(2, EdgeType.PARENT)] }, 2: { "name": "Autolink on me", "edge": [(3, EdgeType.PARENT)] }, 3: { "name": "Do NOT link me please", "edge": [] }, }
def test_no_message_on_good_add(self): self.goals = self.build(open_(1, "Root", select=selected)) self.goals.accept(Add("Success")) assert self.messages == []
def test_link_events(self): self.goals.accept_all(Add("Next"), Add("More"), Select(2), HoldSelect(), Select(3), ToggleLink()) assert self.goals.events()[-1] == ("link", 2, 3, EdgeType.BLOCKER) self.goals.accept(ToggleLink()) assert self.goals.events()[-1] == ("unlink", 2, 3, EdgeType.BLOCKER)
def test_delete_events(self): self.goals.accept_all(Add("Sheep"), Select(2), Delete()) assert self.goals.events()[-3] == ("delete", 2) assert self.goals.events()[-2] == ("select", 1) assert self.goals.events()[-1] == ("hold_select", 1)
def test_select_events(self): self.goals.accept_all(Add("Next"), Select(2)) assert self.goals.events()[-1] == ("select", 2) self.goals.accept_all(HoldSelect(), Select(1)) assert self.goals.events()[-2] == ("hold_select", 2) assert self.goals.events()[-1] == ("select", 1)
def test_add_events(self): assert self.goals.events().pop() == ("add", 1, "Root", True) self.goals.accept(Add("Next")) assert self.goals.events()[-2] == ("add", 2, "Next", True) assert self.goals.events()[-1] == ("link", 1, 2, EdgeType.PARENT)
def goal_chain_11(goal_chain_10): """a → b → c → ... → j → k""" goals = goal_chain_10 goals.accept_all(Select(10), Add("k"), Select(1)) return goals
return result def _switchable(self, key: int) -> bool: if key in self.closed: if back_edges := self._back_edges(key): return any(e.source not in self.closed for e in back_edges) return True return all(x.target in self.closed for x in self._forward_edges(key)) def accept_Insert(self, command: Insert): if (lower := self.previous_selection) == (upper := self.selection): self.error( "A new goal can be inserted only between two different goals") return edge_type = self.edges_forward[lower].get(upper, EdgeType.BLOCKER) if self.accept_Add(Add(command.name, lower, edge_type)): key = len(self.goals) self.accept_ToggleLink(ToggleLink(key, upper, edge_type)) if self._has_link(lower, upper): self.accept_ToggleLink(ToggleLink(lower, upper)) def accept_Rename(self, command: Rename): goal_id = command.goal_id or self.selection self.goals[goal_id] = command.new_name self._events.append(("rename", command.new_name, goal_id)) def accept_ToggleClose(self, command: ToggleClose) -> None: if self.selection in self.closed: if self._may_be_reopened(): self.closed.remove(self.selection) self._events.append(("toggle_close", True, self.selection))
def test_message_on_wrong_add(self): self.goals = self.build(clos_(1, "Root", select=selected)) self.goals.accept(Add("Failed")) assert len(self.messages) == 1
def emit_add(self, text): self.goals.accept(Add(text))
def test_rename_goal(self): self.goals.accept_all(Add("Boom"), Select(2), Rename("A")) assert self.goals.q() == {1: {"name": "Root"}, 2: {"name": "A"}}
def add_goal(self): event("add") self._accept(Add("a"))