def test_apply_mapping_for_the_10th_element(): goals = Goals('a') for i, c in enumerate('bcdefghij'): goals.add(c) goals.select(i + 2) e = Enumeration(goals) assert e.all(keys='name,edge') == { 1: {'name': 'a', 'edge': [2]}, 2: {'name': 'b', 'edge': [3]}, 3: {'name': 'c', 'edge': [4]}, 4: {'name': 'd', 'edge': [5]}, 5: {'name': 'e', 'edge': [6]}, 6: {'name': 'f', 'edge': [7]}, 7: {'name': 'g', 'edge': [8]}, 8: {'name': 'h', 'edge': [9]}, 9: {'name': 'i', 'edge': [0]}, 0: {'name': 'j', 'edge': []}, } # simulate goal addition goals.select(1) goals.add('k') assert e.all(keys='name,edge') == { 11: {'name': 'a', 'edge': [12, 21]}, 12: {'name': 'b', 'edge': [13]}, 13: {'name': 'c', 'edge': [14]}, 14: {'name': 'd', 'edge': [15]}, 15: {'name': 'e', 'edge': [16]}, 16: {'name': 'f', 'edge': [17]}, 17: {'name': 'g', 'edge': [18]}, 18: {'name': 'h', 'edge': [19]}, 19: {'name': 'i', 'edge': [10]}, 10: {'name': 'j', 'edge': []}, 21: {'name': 'k', 'edge': []}, }
def test_select_goal_by_full_id(): goals = Goals('a') for c in 'bcdefghijk': goals.add(c) e = Enumeration(goals) assert e.all(keys='name,select') == { 11: {'name': 'a', 'select': 'select'}, 12: {'name': 'b', 'select': None}, 13: {'name': 'c', 'select': None}, 14: {'name': 'd', 'select': None}, 15: {'name': 'e', 'select': None}, 16: {'name': 'f', 'select': None}, 17: {'name': 'g', 'select': None}, 18: {'name': 'h', 'select': None}, 19: {'name': 'i', 'select': None}, 10: {'name': 'j', 'select': None}, 21: {'name': 'k', 'select': None}, } e.select(13) assert e.all(keys='name,select') == { 11: {'name': 'a', 'select': 'prev'}, 12: {'name': 'b', 'select': None}, 13: {'name': 'c', 'select': 'select'}, 14: {'name': 'd', 'select': None}, 15: {'name': 'e', 'select': None}, 16: {'name': 'f', 'select': None}, 17: {'name': 'g', 'select': None}, 18: {'name': 'h', 'select': None}, 19: {'name': 'i', 'select': None}, 10: {'name': 'j', 'select': None}, 21: {'name': 'k', 'select': None}, }
def test_all_keys_in_enumeration_must_be_of_the_same_length(): g = Goals('Root') for i in range(2999): g.add(str(i)) e = Enumeration(g) mapping = e.all() assert len(mapping) == len(g.all()) assert set(len(str(k)) for k in mapping) == {4}
def test_all_goal_names_must_be_saved_correctly(name): g = Goals('renamed') g.rename(name) with closing(sqlite3.connect(':memory:')) as conn: note(g.events) run_migrations(conn) save_updates(g, conn) ng = build_goals(conn) assert g.all('name,open,edge,select') == ng.all( 'name,open,edge,select')
def build_from(actions, choice_fn, show_notes=True): g = Goals('Root') try: for name in actions: int_val = 0 if name == 'select': int_val = choice_fn(list(g.all().keys())) USER_ACTIONS[name](g, int_val) finally: if show_notes: note(actions) return g
def load(filename=DEFAULT_DB): if path.isfile(filename): connection = sqlite3.connect(filename) run_migrations(connection) cur = connection.cursor() goals = [row for row in cur.execute('select * from goals')] edges = [row for row in cur.execute('select * from edges')] settings = [row for row in cur.execute('select * from settings')] cur.close() goals = Goals.build(goals, edges, settings) else: goals = Goals('Rename me') return Enumeration(Zoom(goals))
def test_mapping_for_top(): goals = Goals('a') goals.add('b') for i, c in enumerate('cdefghijklmnopqrstuv'): goals.add(c) goals.delete(i + 3) # 1: a, 2: b, 3 (i==0): c, 4 (i==1): d, ... goals.add('x') e = Enumeration(goals) assert e.all(keys='name,switchable,select') == { 1: {'name': 'a', 'switchable': False, 'select': 'select'}, 2: {'name': 'b', 'switchable': True, 'select': None}, 3: {'name': 'x', 'switchable': True, 'select': None}, }
def test_selection_cache_should_avoid_overflow(): g = Goals('Root') for i in range(10): g.add(str(i+2)) e = Enumeration(g) assert e.all(keys='select')[11] == {'select': 'select'} e.select(5) assert e.all(keys='select')[11] == {'select': 'select'} e.select(1) assert e.all(keys='select')[11] == {'select': 'select'} assert e.all(keys='select')[14] == {'select': None} e.select(4) assert e.all(keys='select')[11] == {'select': 'prev'} assert e.all(keys='select')[14] == {'select': 'select'}
def test_goaltree_previous_selection_may_be_changed_in_top_view(): goals = Goals('Root') goals.add('Top 1') goals.add('Top 2') goals.hold_select() goals.select(2) e = Enumeration(goals) assert e.all(keys='name,switchable,select') == { 1: {'name': 'Root', 'switchable': False, 'select': 'prev'}, 2: {'name': 'Top 1', 'switchable': True, 'select': 'select'}, 3: {'name': 'Top 2', 'switchable': True, 'select': None}, } e.next_view() assert e.events[-1] == ('hold_select', 2) assert e.all(keys='name,switchable,select') == { 1: {'name': 'Top 1', 'switchable': True, 'select': 'select'}, 2: {'name': 'Top 2', 'switchable': True, 'select': None} } e.insert('Illegal goal') # New goal must not be inserted because previous selection is reset after the view switching e.next_view() assert e.all(keys='name,switchable,select') == { 1: {'name': 'Root', 'switchable': False, 'select': None}, 2: {'name': 'Top 1', 'switchable': True, 'select': 'select'}, 3: {'name': 'Top 2', 'switchable': True, 'select': None}, }
def build_goaltree(*goal_prototypes, message_fn=None): goals = [(g.goal_id, g.name, g.open) for g in goal_prototypes] edges = [(g.goal_id, e, EdgeType.PARENT) for g in goal_prototypes for e in g.children] + [(g.goal_id, e, EdgeType.BLOCKER) for g in goal_prototypes for e in g.blockers] selection = {g.goal_id for g in goal_prototypes if g.select == selected} prev_selection = { g.goal_id for g in goal_prototypes if g.select == previous } assert len(selection) == 1 assert len(prev_selection) <= 1 selection_id = selection.pop() return Goals.build( goals, edges, [ ("selection", selection_id), ( "previous_selection", prev_selection.pop() if prev_selection else selection_id, ), ], message_fn, )
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_dot_export_open_view(): g = Enumeration(Goals('Root')) g.add('Middle') g.add('Top', 2) g.add('Closed') g.select(4) g.toggle_close() assert dot_export(g) == '''digraph g {
def test_goaltree_selection_may_be_changed_in_top_view(): goals = Goals('Root') goals.add('Top 1') goals.add('Top 2') e = Enumeration(goals) assert e.all(keys='name,switchable,select') == { 1: {'name': 'Root', 'switchable': False, 'select': 'select'}, 2: {'name': 'Top 1', 'switchable': True, 'select': None}, 3: {'name': 'Top 2', 'switchable': True, 'select': None}, } e.next_view() assert e.events[-2] == ('select', 2) assert e.events[-1] == ('hold_select', 2) assert e.all(keys='name,switchable,select') == { 1: {'name': 'Top 1', 'switchable': True, 'select': 'select'}, 2: {'name': 'Top 2', 'switchable': True, 'select': None} }
def test_save_into_sqlite3_database(): file_name = NamedTemporaryFile().name goals = all_layers(Goals("Sample")) save(goals, file_name) with sqlite3.connect(file_name) as conn: with closing(conn.cursor()) as cur: cur.execute("select version from migrations") assert cur.fetchone()[0] > 0
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_toggle_switch_view(): e = Enumeration(Goals('Root')) assert e.view == 'open' e.next_view() assert e.view == 'top' e.next_view() assert e.view == 'full' e.next_view() assert e.view == 'open'
def build_goals(conn): with closing(conn.cursor()) as cur: goals = [row for row in cur.execute('select * from goals')] edges = [row for row in cur.execute('select * from edges')] selection = [row for row in cur.execute('select * from settings')] note(goals) note(edges) note(selection) return Goals.build(goals, edges, selection)
def test_simple_top_enumeration_workflow(): e = Enumeration(Goals('root')) e.add('1') e.add('2') e.select(2) e.next_view() e.select(2) assert e.all() == { 1: {'name': '1'}, 2: {'name': '2'} }
def build_goals(conn): with closing(conn.cursor()) as cur: goals = list(cur.execute("select * from goals")) edges = list(cur.execute("select parent, child, reltype from edges")) db_settings = list(cur.execute("select * from settings")) zoom_data = list(cur.execute("select * from zoom")) autolink_data = list(cur.execute("select * from autolink")) note( f"Goals: {goals}, Edges: {edges}, Settings: {db_settings}, Zoom: {zoom_data}, Autolink: {autolink_data}" ) goals = Goals.build(goals, edges, db_settings) return all_layers(goals, zoom_data, autolink_data)
def test_dot_export_zoomed_goal_tree(): g = Enumeration(Zoom(Goals('Root goal'))) g.add('Hidden intermediate') g.add('Zoom root', 2) g.add('Hidden neighbour', 2) g.add('Visible top', 3) g.toggle_link(4, 5) g.select(3) for i in range(8): g.add('Additional %d' % (i + 3), 3) g.toggle_zoom() assert dot_export(g) == '''digraph g {
def test_use_mapping_in_selection(): goals = Goals('a') for i, c in enumerate('bcdefghij'): goals.add(c) goals.select(i + 2) e = Enumeration(goals) e.select(0) assert e.all(keys='name,select') == { 1: {'name': 'a', 'select': 'prev'}, 2: {'name': 'b', 'select': None}, 3: {'name': 'c', 'select': None}, 4: {'name': 'd', 'select': None}, 5: {'name': 'e', 'select': None}, 6: {'name': 'f', 'select': None}, 7: {'name': 'g', 'select': None}, 8: {'name': 'h', 'select': None}, 9: {'name': 'i', 'select': None}, 0: {'name': 'j', 'select': 'select'}, } e.add('k') e.select(1) e.select(6) assert e.all(keys='name,select') == { 11: {'name': 'a', 'select': 'prev'}, 12: {'name': 'b', 'select': None}, 13: {'name': 'c', 'select': None}, 14: {'name': 'd', 'select': None}, 15: {'name': 'e', 'select': None}, 16: {'name': 'f', 'select': 'select'}, 17: {'name': 'g', 'select': None}, 18: {'name': 'h', 'select': None}, 19: {'name': 'i', 'select': None}, 10: {'name': 'j', 'select': None}, 21: {'name': 'k', 'select': None}, }
def test_dot_export_top_view(): g = Enumeration(Goals('Root')) g.add('Middle') g.add('Top', 2) g.add('Closed') g.add('More closed', 3) # close 'More closed' g.select(5) g.toggle_close() # close 'Closed' g.select(4) g.toggle_close() g.next_view() assert dot_export(g) == '''digraph g {
def test_simple_open_enumeration_workflow(): e = Enumeration(Goals('Root')) e.add('1') e.add('2') e.select(2) assert e.all(keys='name,select,open,edge') == { 1: {'name': 'Root', 'select': 'prev', 'open': True, 'edge': [2, 3]}, 2: {'name': '1', 'select': 'select', 'open': True, 'edge': []}, 3: {'name': '2', 'select': None, 'open': True, 'edge': []}, } e.toggle_close() assert e.all(keys='name,select,open,edge') == { 1: {'name': 'Root', 'select': 'select', 'open': True, 'edge': [2]}, 2: {'name': '2', 'select': None, 'open': True, 'edge': []} }
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 save(goals, filename=DEFAULT_DB): if path.isfile(filename): connection = sqlite3.connect(filename) run_migrations(connection) save_updates(goals, connection) connection.close() else: connection = sqlite3.connect(filename) run_migrations(connection) goals_export, edges_export, select_export = Goals.export(goals) cur = connection.cursor() cur.executemany('insert into goals values (?,?,?)', goals_export) cur.executemany('insert into edges values (?,?)', edges_export) cur.executemany('insert into settings values (?,?)', select_export) goals.events.clear() connection.commit() connection.close()
def test_migration_must_run_on_load_from_existing_db(): file_name = NamedTemporaryFile().name goals = all_layers(Goals("Just a simple goal tree")) save(goals, file_name) MIGRATIONS.append([ "create table dummy (answer integer)", "insert into dummy values (42)", ]) try: load(file_name) with closing(sqlite3.connect(file_name)) as conn: with closing(conn.cursor()) as cur: cur.execute("select answer from dummy") value = cur.fetchone()[0] assert value == 42 finally: MIGRATIONS.pop(-1)
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_dot_export_full_view(): g = Enumeration(Goals('Root')) g.add('Middle') g.add('Top', 2) g.add('Closed') g.select(4) g.toggle_close() g.next_view() g.next_view() g.select(1) assert dot_export(g) == '''digraph g { node [shape=box]; 1 [label="1: Root", color=red, style=filled, fillcolor=gray]; 2 [label="2: Middle", color=red]; 3 [label="3: Top", color=red, style="bold,filled", fillcolor=lightgray]; 4 [label="4: Closed", color=green]; 2 -> 1 [color=black]; 4 -> 1 [color=gray]; 3 -> 2 [color=black]; }''' g.hold_select() g.select(3) assert dot_export(g) == '''digraph g {
def test_selection_cache_should_be_reset_after_view_switch(): g = Goals('Root') # 1 -> 2 -> 3 -> .. -> 10 -> 11 for i in range(10): g.add(str(i+2), i+1) g.add('Also top', 1) e = Enumeration(g) e.select(1) e.next_view() assert e.all('name,select') == { 1: {'name': '11', 'select': 'select'}, 2: {'name': 'Also top', 'select': None}, } e.select(2) assert e.all('name,select') == { 1: {'name': '11', 'select': 'prev'}, 2: {'name': 'Also top', 'select': 'select'}, }
def setUp(self): self.messages = [] self.goals = Goals("Root", self._register_message)