591
591
def test__remove_simple_descendants(self):
592
592
graph = self.make_graph(ancestry_1)
593
self.assertRemoveDescendants({'rev1'}, graph,
594
{'rev1', 'rev2a', 'rev2b', 'rev3', 'rev4'})
593
self.assertRemoveDescendants({b'rev1'}, graph,
594
{b'rev1', b'rev2a', b'rev2b', b'rev3', b'rev4'})
596
596
def test__remove_simple_descendants_disjoint(self):
597
597
graph = self.make_graph(ancestry_1)
598
self.assertRemoveDescendants({'rev1', 'rev3'}, graph,
598
self.assertRemoveDescendants({b'rev1', b'rev3'}, graph,
601
601
def test__remove_simple_descendants_chain(self):
602
602
graph = self.make_graph(ancestry_1)
603
self.assertRemoveDescendants({'rev1'}, graph,
604
{'rev1', 'rev2a', 'rev3'})
603
self.assertRemoveDescendants({b'rev1'}, graph,
604
{b'rev1', b'rev2a', b'rev3'})
606
606
def test__remove_simple_descendants_siblings(self):
607
607
graph = self.make_graph(ancestry_1)
608
self.assertRemoveDescendants({'rev2a', 'rev2b'}, graph,
609
{'rev2a', 'rev2b', 'rev3'})
608
self.assertRemoveDescendants({b'rev2a', b'rev2b'}, graph,
609
{b'rev2a', b'rev2b', b'rev3'})
611
611
def test_unique_lca_criss_cross(self):
612
612
"""Ensure we don't pick non-unique lcas in a criss-cross"""
613
613
graph = self.make_graph(criss_cross)
614
self.assertEqual('rev1', graph.find_unique_lca('rev3a', 'rev3b'))
615
lca, steps = graph.find_unique_lca('rev3a', 'rev3b', count_steps=True)
616
self.assertEqual('rev1', lca)
614
self.assertEqual(b'rev1', graph.find_unique_lca(b'rev3a', b'rev3b'))
615
lca, steps = graph.find_unique_lca(b'rev3a', b'rev3b', count_steps=True)
616
self.assertEqual(b'rev1', lca)
617
617
self.assertEqual(2, steps)
619
619
def test_unique_lca_null_revision(self):
620
620
"""Ensure we pick NULL_REVISION when necessary"""
621
621
graph = self.make_graph(criss_cross2)
622
self.assertEqual('rev1b', graph.find_unique_lca('rev2a', 'rev1b'))
622
self.assertEqual(b'rev1b', graph.find_unique_lca(b'rev2a', b'rev1b'))
623
623
self.assertEqual(NULL_REVISION,
624
graph.find_unique_lca('rev2a', 'rev2b'))
624
graph.find_unique_lca(b'rev2a', b'rev2b'))
626
626
def test_unique_lca_null_revision2(self):
627
627
"""Ensure we pick NULL_REVISION when necessary"""
628
628
graph = self.make_graph(ancestry_2)
629
629
self.assertEqual(NULL_REVISION,
630
graph.find_unique_lca('rev4a', 'rev1b'))
630
graph.find_unique_lca(b'rev4a', b'rev1b'))
632
632
def test_lca_double_shortcut(self):
633
633
graph = self.make_graph(double_shortcut)
634
self.assertEqual('c', graph.find_unique_lca('f', 'g'))
634
self.assertEqual(b'c', graph.find_unique_lca(b'f', b'g'))
636
636
def test_common_ancestor_two_repos(self):
637
637
"""Ensure we do unique_lca using data from two repos"""
648
648
graph = mainline_tree.branch.repository.get_graph(
649
649
feature_tree.branch.repository)
650
self.assertEqual('rev2b', graph.find_unique_lca('rev2a', 'rev3b'))
650
self.assertEqual(b'rev2b', graph.find_unique_lca(b'rev2a', b'rev3b'))
652
652
def test_graph_difference(self):
653
653
graph = self.make_graph(ancestry_1)
654
self.assertEqual((set(), set()), graph.find_difference('rev1', 'rev1'))
655
self.assertEqual((set(), {'rev1'}),
656
graph.find_difference(NULL_REVISION, 'rev1'))
657
self.assertEqual(({'rev1'}, set()),
658
graph.find_difference('rev1', NULL_REVISION))
659
self.assertEqual(({'rev2a', 'rev3'}, {'rev2b'}),
660
graph.find_difference('rev3', 'rev2b'))
661
self.assertEqual(({'rev4', 'rev3', 'rev2a'}, set()),
662
graph.find_difference('rev4', 'rev2b'))
654
self.assertEqual((set(), set()), graph.find_difference(b'rev1', b'rev1'))
655
self.assertEqual((set(), {b'rev1'}),
656
graph.find_difference(NULL_REVISION, b'rev1'))
657
self.assertEqual(({b'rev1'}, set()),
658
graph.find_difference(b'rev1', NULL_REVISION))
659
self.assertEqual(({b'rev2a', b'rev3'}, {b'rev2b'}),
660
graph.find_difference(b'rev3', b'rev2b'))
661
self.assertEqual(({b'rev4', b'rev3', b'rev2a'}, set()),
662
graph.find_difference(b'rev4', b'rev2b'))
664
664
def test_graph_difference_separate_ancestry(self):
665
665
graph = self.make_graph(ancestry_2)
666
self.assertEqual(({'rev1a'}, {'rev1b'}),
667
graph.find_difference('rev1a', 'rev1b'))
668
self.assertEqual(({'rev1a', 'rev2a', 'rev3a', 'rev4a'},
670
graph.find_difference('rev4a', 'rev1b'))
666
self.assertEqual(({b'rev1a'}, {b'rev1b'}),
667
graph.find_difference(b'rev1a', b'rev1b'))
668
self.assertEqual(({b'rev1a', b'rev2a', b'rev3a', b'rev4a'},
670
graph.find_difference(b'rev4a', b'rev1b'))
672
672
def test_graph_difference_criss_cross(self):
673
673
graph = self.make_graph(criss_cross)
674
self.assertEqual(({'rev3a'}, {'rev3b'}),
675
graph.find_difference('rev3a', 'rev3b'))
676
self.assertEqual((set([]), {'rev3b', 'rev2b'}),
677
graph.find_difference('rev2a', 'rev3b'))
674
self.assertEqual(({b'rev3a'}, {b'rev3b'}),
675
graph.find_difference(b'rev3a', b'rev3b'))
676
self.assertEqual((set([]), {b'rev3b', b'rev2b'}),
677
graph.find_difference(b'rev2a', b'rev3b'))
679
679
def test_graph_difference_extended_history(self):
680
680
graph = self.make_graph(extended_history_shortcut)
681
self.assertEqual(({'e'}, {'f'}),
682
graph.find_difference('e', 'f'))
683
self.assertEqual(({'f'}, {'e'}),
684
graph.find_difference('f', 'e'))
681
self.assertEqual(({b'e'}, {b'f'}),
682
graph.find_difference(b'e', b'f'))
683
self.assertEqual(({b'f'}, {b'e'}),
684
graph.find_difference(b'f', b'e'))
686
686
def test_graph_difference_double_shortcut(self):
687
687
graph = self.make_graph(double_shortcut)
688
self.assertEqual(({'d', 'f'}, {'e', 'g'}),
689
graph.find_difference('f', 'g'))
688
self.assertEqual(({b'd', b'f'}, {b'e', b'g'}),
689
graph.find_difference(b'f', b'g'))
691
691
def test_graph_difference_complex_shortcut(self):
692
692
graph = self.make_graph(complex_shortcut)
693
self.assertEqual(({'m', 'i', 'e'}, {'n', 'h'}),
694
graph.find_difference('m', 'n'))
693
self.assertEqual(({b'm', b'i', b'e'}, {b'n', b'h'}),
694
graph.find_difference(b'm', b'n'))
696
696
def test_graph_difference_complex_shortcut2(self):
697
697
graph = self.make_graph(complex_shortcut2)
698
self.assertEqual(({'t'}, {'j', 'u'}),
699
graph.find_difference('t', 'u'))
698
self.assertEqual(({b't'}, {b'j', b'u'}),
699
graph.find_difference(b't', b'u'))
701
701
def test_graph_difference_shortcut_extra_root(self):
702
702
graph = self.make_graph(shortcut_extra_root)
703
self.assertEqual(({'e'}, {'f', 'g'}),
704
graph.find_difference('e', 'f'))
703
self.assertEqual(({b'e'}, {b'f', b'g'}),
704
graph.find_difference(b'e', b'f'))
706
706
def test_iter_topo_order(self):
707
707
graph = self.make_graph(ancestry_1)
708
args = ['rev2a', 'rev3', 'rev1']
708
args = [b'rev2a', b'rev3', b'rev1']
709
709
topo_args = list(graph.iter_topo_order(args))
710
710
self.assertEqual(set(args), set(topo_args))
711
self.assertTrue(topo_args.index('rev2a') > topo_args.index('rev1'))
712
self.assertTrue(topo_args.index('rev2a') < topo_args.index('rev3'))
711
self.assertTrue(topo_args.index(b'rev2a') > topo_args.index(b'rev1'))
712
self.assertTrue(topo_args.index(b'rev2a') < topo_args.index(b'rev3'))
714
714
def test_is_ancestor(self):
715
715
graph = self.make_graph(ancestry_1)
716
self.assertEqual(True, graph.is_ancestor('null:', 'null:'))
717
self.assertEqual(True, graph.is_ancestor('null:', 'rev1'))
718
self.assertEqual(False, graph.is_ancestor('rev1', 'null:'))
719
self.assertEqual(True, graph.is_ancestor('null:', 'rev4'))
720
self.assertEqual(False, graph.is_ancestor('rev4', 'null:'))
721
self.assertEqual(False, graph.is_ancestor('rev4', 'rev2b'))
722
self.assertEqual(True, graph.is_ancestor('rev2b', 'rev4'))
723
self.assertEqual(False, graph.is_ancestor('rev2b', 'rev3'))
724
self.assertEqual(False, graph.is_ancestor('rev3', 'rev2b'))
716
self.assertEqual(True, graph.is_ancestor(b'null:', b'null:'))
717
self.assertEqual(True, graph.is_ancestor(b'null:', b'rev1'))
718
self.assertEqual(False, graph.is_ancestor(b'rev1', b'null:'))
719
self.assertEqual(True, graph.is_ancestor(b'null:', b'rev4'))
720
self.assertEqual(False, graph.is_ancestor(b'rev4', b'null:'))
721
self.assertEqual(False, graph.is_ancestor(b'rev4', b'rev2b'))
722
self.assertEqual(True, graph.is_ancestor(b'rev2b', b'rev4'))
723
self.assertEqual(False, graph.is_ancestor(b'rev2b', b'rev3'))
724
self.assertEqual(False, graph.is_ancestor(b'rev3', b'rev2b'))
725
725
instrumented_provider = InstrumentedParentsProvider(graph)
726
726
instrumented_graph = _mod_graph.Graph(instrumented_provider)
727
instrumented_graph.is_ancestor('rev2a', 'rev2b')
728
self.assertTrue('null:' not in instrumented_provider.calls)
727
instrumented_graph.is_ancestor(b'rev2a', b'rev2b')
728
self.assertTrue(b'null:' not in instrumented_provider.calls)
730
730
def test_is_between(self):
731
731
graph = self.make_graph(ancestry_1)
732
self.assertEqual(True, graph.is_between('null:', 'null:', 'null:'))
733
self.assertEqual(True, graph.is_between('rev1', 'null:', 'rev1'))
734
self.assertEqual(True, graph.is_between('rev1', 'rev1', 'rev4'))
735
self.assertEqual(True, graph.is_between('rev4', 'rev1', 'rev4'))
736
self.assertEqual(True, graph.is_between('rev3', 'rev1', 'rev4'))
737
self.assertEqual(False, graph.is_between('rev4', 'rev1', 'rev3'))
738
self.assertEqual(False, graph.is_between('rev1', 'rev2a', 'rev4'))
739
self.assertEqual(False, graph.is_between('null:', 'rev1', 'rev4'))
732
self.assertEqual(True, graph.is_between(b'null:', b'null:', b'null:'))
733
self.assertEqual(True, graph.is_between(b'rev1', b'null:', b'rev1'))
734
self.assertEqual(True, graph.is_between(b'rev1', b'rev1', b'rev4'))
735
self.assertEqual(True, graph.is_between(b'rev4', b'rev1', b'rev4'))
736
self.assertEqual(True, graph.is_between(b'rev3', b'rev1', b'rev4'))
737
self.assertEqual(False, graph.is_between(b'rev4', b'rev1', b'rev3'))
738
self.assertEqual(False, graph.is_between(b'rev1', b'rev2a', b'rev4'))
739
self.assertEqual(False, graph.is_between(b'null:', b'rev1', b'rev4'))
741
741
def test_is_ancestor_boundary(self):
742
742
"""Ensure that we avoid searching the whole graph.
792
graph = self.make_graph({'c': ['b', 'd'], 'd': ['e'], 'b': ['a'],
793
'a': [NULL_REVISION], 'e': [NULL_REVISION]})
794
self.assertEqual({'c'}, graph.heads(['a', 'c', 'e']))
792
graph = self.make_graph({b'c': [b'b', b'd'], b'd': [b'e'], b'b': [b'a'],
793
b'a': [NULL_REVISION], b'e': [NULL_REVISION]})
794
self.assertEqual({b'c'}, graph.heads([b'a', b'c', b'e']))
796
796
def test_heads_null(self):
797
797
graph = self.make_graph(ancestry_1)
798
self.assertEqual({'null:'}, graph.heads(['null:']))
799
self.assertEqual({'rev1'}, graph.heads(['null:', 'rev1']))
800
self.assertEqual({'rev1'}, graph.heads(['rev1', 'null:']))
801
self.assertEqual({'rev1'}, graph.heads({'rev1', 'null:'}))
802
self.assertEqual({'rev1'}, graph.heads(('rev1', 'null:')))
798
self.assertEqual({b'null:'}, graph.heads([b'null:']))
799
self.assertEqual({b'rev1'}, graph.heads([b'null:', b'rev1']))
800
self.assertEqual({b'rev1'}, graph.heads([b'rev1', b'null:']))
801
self.assertEqual({b'rev1'}, graph.heads({b'rev1', b'null:'}))
802
self.assertEqual({b'rev1'}, graph.heads((b'rev1', b'null:')))
804
804
def test_heads_one(self):
805
805
# A single node will always be a head
806
806
graph = self.make_graph(ancestry_1)
807
self.assertEqual({'null:'}, graph.heads(['null:']))
808
self.assertEqual({'rev1'}, graph.heads(['rev1']))
809
self.assertEqual({'rev2a'}, graph.heads(['rev2a']))
810
self.assertEqual({'rev2b'}, graph.heads(['rev2b']))
811
self.assertEqual({'rev3'}, graph.heads(['rev3']))
812
self.assertEqual({'rev4'}, graph.heads(['rev4']))
807
self.assertEqual({b'null:'}, graph.heads([b'null:']))
808
self.assertEqual({b'rev1'}, graph.heads([b'rev1']))
809
self.assertEqual({b'rev2a'}, graph.heads([b'rev2a']))
810
self.assertEqual({b'rev2b'}, graph.heads([b'rev2b']))
811
self.assertEqual({b'rev3'}, graph.heads([b'rev3']))
812
self.assertEqual({b'rev4'}, graph.heads([b'rev4']))
814
814
def test_heads_single(self):
815
815
graph = self.make_graph(ancestry_1)
816
self.assertEqual({'rev4'}, graph.heads(['null:', 'rev4']))
817
self.assertEqual({'rev2a'}, graph.heads(['rev1', 'rev2a']))
818
self.assertEqual({'rev2b'}, graph.heads(['rev1', 'rev2b']))
819
self.assertEqual({'rev3'}, graph.heads(['rev1', 'rev3']))
820
self.assertEqual({'rev4'}, graph.heads(['rev1', 'rev4']))
821
self.assertEqual({'rev4'}, graph.heads(['rev2a', 'rev4']))
822
self.assertEqual({'rev4'}, graph.heads(['rev2b', 'rev4']))
823
self.assertEqual({'rev4'}, graph.heads(['rev3', 'rev4']))
816
self.assertEqual({b'rev4'}, graph.heads([b'null:', b'rev4']))
817
self.assertEqual({b'rev2a'}, graph.heads([b'rev1', b'rev2a']))
818
self.assertEqual({b'rev2b'}, graph.heads([b'rev1', b'rev2b']))
819
self.assertEqual({b'rev3'}, graph.heads([b'rev1', b'rev3']))
820
self.assertEqual({b'rev4'}, graph.heads([b'rev1', b'rev4']))
821
self.assertEqual({b'rev4'}, graph.heads([b'rev2a', b'rev4']))
822
self.assertEqual({b'rev4'}, graph.heads([b'rev2b', b'rev4']))
823
self.assertEqual({b'rev4'}, graph.heads([b'rev3', b'rev4']))
825
825
def test_heads_two_heads(self):
826
826
graph = self.make_graph(ancestry_1)
827
self.assertEqual({'rev2a', 'rev2b'},
828
graph.heads(['rev2a', 'rev2b']))
829
self.assertEqual({'rev3', 'rev2b'},
830
graph.heads(['rev3', 'rev2b']))
827
self.assertEqual({b'rev2a', b'rev2b'},
828
graph.heads([b'rev2a', b'rev2b']))
829
self.assertEqual({b'rev3', b'rev2b'},
830
graph.heads([b'rev3', b'rev2b']))
832
832
def test_heads_criss_cross(self):
833
833
graph = self.make_graph(criss_cross)
834
self.assertEqual({'rev2a'},
835
graph.heads(['rev2a', 'rev1']))
836
self.assertEqual({'rev2b'},
837
graph.heads(['rev2b', 'rev1']))
838
self.assertEqual({'rev3a'},
839
graph.heads(['rev3a', 'rev1']))
840
self.assertEqual({'rev3b'},
841
graph.heads(['rev3b', 'rev1']))
842
self.assertEqual({'rev2a', 'rev2b'},
843
graph.heads(['rev2a', 'rev2b']))
844
self.assertEqual({'rev3a'},
845
graph.heads(['rev3a', 'rev2a']))
846
self.assertEqual({'rev3a'},
847
graph.heads(['rev3a', 'rev2b']))
848
self.assertEqual({'rev3a'},
849
graph.heads(['rev3a', 'rev2a', 'rev2b']))
850
self.assertEqual({'rev3b'},
851
graph.heads(['rev3b', 'rev2a']))
852
self.assertEqual({'rev3b'},
853
graph.heads(['rev3b', 'rev2b']))
854
self.assertEqual({'rev3b'},
855
graph.heads(['rev3b', 'rev2a', 'rev2b']))
856
self.assertEqual({'rev3a', 'rev3b'},
857
graph.heads(['rev3a', 'rev3b']))
858
self.assertEqual({'rev3a', 'rev3b'},
859
graph.heads(['rev3a', 'rev3b', 'rev2a', 'rev2b']))
834
self.assertEqual({b'rev2a'},
835
graph.heads([b'rev2a', b'rev1']))
836
self.assertEqual({b'rev2b'},
837
graph.heads([b'rev2b', b'rev1']))
838
self.assertEqual({b'rev3a'},
839
graph.heads([b'rev3a', b'rev1']))
840
self.assertEqual({b'rev3b'},
841
graph.heads([b'rev3b', b'rev1']))
842
self.assertEqual({b'rev2a', b'rev2b'},
843
graph.heads([b'rev2a', b'rev2b']))
844
self.assertEqual({b'rev3a'},
845
graph.heads([b'rev3a', b'rev2a']))
846
self.assertEqual({b'rev3a'},
847
graph.heads([b'rev3a', b'rev2b']))
848
self.assertEqual({b'rev3a'},
849
graph.heads([b'rev3a', b'rev2a', b'rev2b']))
850
self.assertEqual({b'rev3b'},
851
graph.heads([b'rev3b', b'rev2a']))
852
self.assertEqual({b'rev3b'},
853
graph.heads([b'rev3b', b'rev2b']))
854
self.assertEqual({b'rev3b'},
855
graph.heads([b'rev3b', b'rev2a', b'rev2b']))
856
self.assertEqual({b'rev3a', b'rev3b'},
857
graph.heads([b'rev3a', b'rev3b']))
858
self.assertEqual({b'rev3a', b'rev3b'},
859
graph.heads([b'rev3a', b'rev3b', b'rev2a', b'rev2b']))
861
861
def test_heads_shortcut(self):
862
862
graph = self.make_graph(history_shortcut)
864
self.assertEqual({'rev2a', 'rev2b', 'rev2c'},
865
graph.heads(['rev2a', 'rev2b', 'rev2c']))
866
self.assertEqual({'rev3a', 'rev3b'},
867
graph.heads(['rev3a', 'rev3b']))
868
self.assertEqual({'rev3a', 'rev3b'},
869
graph.heads(['rev2a', 'rev3a', 'rev3b']))
870
self.assertEqual({'rev2a', 'rev3b'},
871
graph.heads(['rev2a', 'rev3b']))
872
self.assertEqual({'rev2c', 'rev3a'},
873
graph.heads(['rev2c', 'rev3a']))
864
self.assertEqual({b'rev2a', b'rev2b', b'rev2c'},
865
graph.heads([b'rev2a', b'rev2b', b'rev2c']))
866
self.assertEqual({b'rev3a', b'rev3b'},
867
graph.heads([b'rev3a', b'rev3b']))
868
self.assertEqual({b'rev3a', b'rev3b'},
869
graph.heads([b'rev2a', b'rev3a', b'rev3b']))
870
self.assertEqual({b'rev2a', b'rev3b'},
871
graph.heads([b'rev2a', b'rev3b']))
872
self.assertEqual({b'rev2c', b'rev3a'},
873
graph.heads([b'rev2c', b'rev3a']))
875
875
def _run_heads_break_deeper(self, graph_dict, search):
876
876
"""Run heads on a graph-as-a-dict.
878
If the search asks for the parents of 'deeper' the test will fail.
878
If the search asks for the parents of b'deeper' the test will fail.
880
880
class stub(object):
882
882
def get_parent_map(keys):
886
886
self.fail('key deeper was accessed')
887
887
result[key] = graph_dict[key]
894
894
def test_heads_limits_search(self):
895
895
# test that a heads query does not search all of history
899
'common': ['deeper'],
897
b'left': [b'common'],
898
b'right': [b'common'],
899
b'common': [b'deeper'],
901
self.assertEqual({'left', 'right'},
902
self._run_heads_break_deeper(graph_dict, ['left', 'right']))
901
self.assertEqual({b'left', b'right'},
902
self._run_heads_break_deeper(graph_dict, [b'left', b'right']))
904
904
def test_heads_limits_search_assymetric(self):
905
905
# test that a heads query does not search all of history
908
'midleft': ['common'],
910
'common': ['aftercommon'],
911
'aftercommon': ['deeper'],
907
b'left': [b'midleft'],
908
b'midleft': [b'common'],
909
b'right': [b'common'],
910
b'common': [b'aftercommon'],
911
b'aftercommon': [b'deeper'],
913
self.assertEqual({'left', 'right'},
914
self._run_heads_break_deeper(graph_dict, ['left', 'right']))
913
self.assertEqual({b'left', b'right'},
914
self._run_heads_break_deeper(graph_dict, [b'left', b'right']))
916
916
def test_heads_limits_search_common_search_must_continue(self):
917
917
# test that common nodes are still queried, preventing
918
918
# all-the-way-to-origin behaviour in the following graph:
920
'h1': ['shortcut', 'common1'],
922
'shortcut': ['common2'],
923
'common1': ['common2'],
924
'common2': ['deeper'],
920
b'h1': [b'shortcut', b'common1'],
922
b'shortcut': [b'common2'],
923
b'common1': [b'common2'],
924
b'common2': [b'deeper'],
926
self.assertEqual({'h1', 'h2'},
927
self._run_heads_break_deeper(graph_dict, ['h1', 'h2']))
926
self.assertEqual({b'h1', b'h2'},
927
self._run_heads_break_deeper(graph_dict, [b'h1', b'h2']))
929
929
def test_breadth_first_search_start_ghosts(self):
930
930
graph = self.make_graph({})
931
931
# with_ghosts reports the ghosts
932
search = graph._make_breadth_first_searcher(['a-ghost'])
933
self.assertEqual((set(), {'a-ghost'}), search.next_with_ghosts())
932
search = graph._make_breadth_first_searcher([b'a-ghost'])
933
self.assertEqual((set(), {b'a-ghost'}), search.next_with_ghosts())
934
934
self.assertRaises(StopIteration, search.next_with_ghosts)
935
935
# next includes them
936
search = graph._make_breadth_first_searcher(['a-ghost'])
937
self.assertEqual({'a-ghost'}, next(search))
936
search = graph._make_breadth_first_searcher([b'a-ghost'])
937
self.assertEqual({b'a-ghost'}, next(search))
938
938
self.assertRaises(StopIteration, next, search)
940
940
def test_breadth_first_search_deep_ghosts(self):
941
941
graph = self.make_graph({
943
'present': ['child', 'ghost'],
942
b'head': [b'present'],
943
b'present': [b'child', b'ghost'],
946
946
# with_ghosts reports the ghosts
947
search = graph._make_breadth_first_searcher(['head'])
948
self.assertEqual(({'head'}, set()), search.next_with_ghosts())
949
self.assertEqual(({'present'}, set()), search.next_with_ghosts())
950
self.assertEqual(({'child'}, {'ghost'}),
947
search = graph._make_breadth_first_searcher([b'head'])
948
self.assertEqual(({b'head'}, set()), search.next_with_ghosts())
949
self.assertEqual(({b'present'}, set()), search.next_with_ghosts())
950
self.assertEqual(({b'child'}, {b'ghost'}),
951
951
search.next_with_ghosts())
952
952
self.assertRaises(StopIteration, search.next_with_ghosts)
953
953
# next includes them
954
search = graph._make_breadth_first_searcher(['head'])
955
self.assertEqual({'head'}, next(search))
956
self.assertEqual({'present'}, next(search))
957
self.assertEqual({'child', 'ghost'},
954
search = graph._make_breadth_first_searcher([b'head'])
955
self.assertEqual({b'head'}, next(search))
956
self.assertEqual({b'present'}, next(search))
957
self.assertEqual({b'child', b'ghost'},
959
959
self.assertRaises(StopIteration, next, search)
962
962
# To make the API robust, we allow calling both next() and
963
963
# next_with_ghosts() on the same searcher.
964
964
graph = self.make_graph({
966
'present': ['child', 'ghost'],
965
b'head': [b'present'],
966
b'present': [b'child', b'ghost'],
969
969
# start with next_with_ghosts
970
search = graph._make_breadth_first_searcher(['head'])
971
self.assertEqual(({'head'}, set()), search.next_with_ghosts())
972
self.assertEqual({'present'}, next(search))
973
self.assertEqual(({'child'}, {'ghost'}),
970
search = graph._make_breadth_first_searcher([b'head'])
971
self.assertEqual(({b'head'}, set()), search.next_with_ghosts())
972
self.assertEqual({b'present'}, next(search))
973
self.assertEqual(({b'child'}, {b'ghost'}),
974
974
search.next_with_ghosts())
975
975
self.assertRaises(StopIteration, next, search)
976
976
# start with next
977
search = graph._make_breadth_first_searcher(['head'])
978
self.assertEqual({'head'}, next(search))
979
self.assertEqual(({'present'}, set()), search.next_with_ghosts())
980
self.assertEqual({'child', 'ghost'},
977
search = graph._make_breadth_first_searcher([b'head'])
978
self.assertEqual({b'head'}, next(search))
979
self.assertEqual(({b'present'}, set()), search.next_with_ghosts())
980
self.assertEqual({b'child', b'ghost'},
982
982
self.assertRaises(StopIteration, search.next_with_ghosts)
984
984
def test_breadth_first_change_search(self):
985
985
# Changing the search should work with both next and next_with_ghosts.
986
986
graph = self.make_graph({
988
'present': ['stopped'],
989
'other': ['other_2'],
987
b'head': [b'present'],
988
b'present': [b'stopped'],
989
b'other': [b'other_2'],
992
search = graph._make_breadth_first_searcher(['head'])
993
self.assertEqual(({'head'}, set()), search.next_with_ghosts())
994
self.assertEqual(({'present'}, set()), search.next_with_ghosts())
995
self.assertEqual({'present'},
996
search.stop_searching_any(['present']))
997
self.assertEqual(({'other'}, {'other_ghost'}),
998
search.start_searching(['other', 'other_ghost']))
999
self.assertEqual(({'other_2'}, set()), search.next_with_ghosts())
992
search = graph._make_breadth_first_searcher([b'head'])
993
self.assertEqual(({b'head'}, set()), search.next_with_ghosts())
994
self.assertEqual(({b'present'}, set()), search.next_with_ghosts())
995
self.assertEqual({b'present'},
996
search.stop_searching_any([b'present']))
997
self.assertEqual(({b'other'}, {b'other_ghost'}),
998
search.start_searching([b'other', b'other_ghost']))
999
self.assertEqual(({b'other_2'}, set()), search.next_with_ghosts())
1000
1000
self.assertRaises(StopIteration, search.next_with_ghosts)
1001
1001
# next includes them
1002
search = graph._make_breadth_first_searcher(['head'])
1003
self.assertEqual({'head'}, next(search))
1004
self.assertEqual({'present'}, next(search))
1005
self.assertEqual({'present'},
1006
search.stop_searching_any(['present']))
1007
search.start_searching(['other', 'other_ghost'])
1008
self.assertEqual({'other_2'}, next(search))
1002
search = graph._make_breadth_first_searcher([b'head'])
1003
self.assertEqual({b'head'}, next(search))
1004
self.assertEqual({b'present'}, next(search))
1005
self.assertEqual({b'present'},
1006
search.stop_searching_any([b'present']))
1007
search.start_searching([b'other', b'other_ghost'])
1008
self.assertEqual({b'other_2'}, next(search))
1009
1009
self.assertRaises(StopIteration, next, search)
1011
1011
def assertSeenAndResult(self, instructions, search, next):
1036
1036
def test_breadth_first_get_result_excludes_current_pending(self):
1037
1037
graph = self.make_graph({
1039
'child': [NULL_REVISION],
1038
b'head': [b'child'],
1039
b'child': [NULL_REVISION],
1040
1040
NULL_REVISION: [],
1042
search = graph._make_breadth_first_searcher(['head'])
1042
search = graph._make_breadth_first_searcher([b'head'])
1043
1043
# At the start, nothing has been seen, to its all excluded:
1044
1044
state = search.get_state()
1045
self.assertEqual(({'head'}, {'head'}, set()),
1045
self.assertEqual(({b'head'}, {b'head'}, set()),
1047
1047
self.assertEqual(set(), search.seen)
1050
({'head'}, ({'head'}, {'child'}, 1),
1051
['head'], None, None),
1052
({'head', 'child'}, ({'head'}, {NULL_REVISION}, 2),
1053
['head', 'child'], None, None),
1054
({'head', 'child', NULL_REVISION}, ({'head'}, set(), 3),
1055
['head', 'child', NULL_REVISION], None, None),
1050
({b'head'}, ({b'head'}, {b'child'}, 1),
1051
[b'head'], None, None),
1052
({b'head', b'child'}, ({b'head'}, {NULL_REVISION}, 2),
1053
[b'head', b'child'], None, None),
1054
({b'head', b'child', NULL_REVISION}, ({b'head'}, set(), 3),
1055
[b'head', b'child', NULL_REVISION], None, None),
1057
1057
self.assertSeenAndResult(expected, search, search.__next__)
1058
1058
# using next_with_ghosts:
1059
search = graph._make_breadth_first_searcher(['head'])
1059
search = graph._make_breadth_first_searcher([b'head'])
1060
1060
self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1062
1062
def test_breadth_first_get_result_starts_stops(self):
1063
1063
graph = self.make_graph({
1065
'child':[NULL_REVISION],
1066
'otherhead':['otherchild'],
1067
'otherchild':['excluded'],
1068
'excluded':[NULL_REVISION],
1065
b'child':[NULL_REVISION],
1066
b'otherhead':[b'otherchild'],
1067
b'otherchild':[b'excluded'],
1068
b'excluded':[NULL_REVISION],
1069
1069
NULL_REVISION:[]
1071
1071
search = graph._make_breadth_first_searcher([])
1072
1072
# Starting with nothing and adding a search works:
1073
search.start_searching(['head'])
1073
search.start_searching([b'head'])
1074
1074
# head has been seen:
1075
1075
state = search.get_state()
1076
self.assertEqual(({'head'}, {'child'}, {'head'}),
1076
self.assertEqual(({b'head'}, {b'child'}, {b'head'}),
1078
self.assertEqual({'head'}, search.seen)
1078
self.assertEqual({b'head'}, search.seen)
1081
1081
# stop at child, and start a new search at otherhead:
1082
1082
# - otherhead counts as seen immediately when start_searching is
1084
({'head', 'child', 'otherhead'},
1085
({'head', 'otherhead'}, {'child', 'otherchild'}, 2),
1086
['head', 'otherhead'], ['otherhead'], ['child']),
1087
({'head', 'child', 'otherhead', 'otherchild'},
1088
({'head', 'otherhead'}, {'child', 'excluded'}, 3),
1089
['head', 'otherhead', 'otherchild'], None, None),
1084
({b'head', b'child', b'otherhead'},
1085
({b'head', b'otherhead'}, {b'child', b'otherchild'}, 2),
1086
[b'head', b'otherhead'], [b'otherhead'], [b'child']),
1087
({b'head', b'child', b'otherhead', b'otherchild'},
1088
({b'head', b'otherhead'}, {b'child', b'excluded'}, 3),
1089
[b'head', b'otherhead', b'otherchild'], None, None),
1090
1090
# stop searching excluded now
1091
({'head', 'child', 'otherhead', 'otherchild', 'excluded'},
1092
({'head', 'otherhead'}, {'child', 'excluded'}, 3),
1093
['head', 'otherhead', 'otherchild'], None, ['excluded']),
1091
({b'head', b'child', b'otherhead', b'otherchild', b'excluded'},
1092
({b'head', b'otherhead'}, {b'child', b'excluded'}, 3),
1093
[b'head', b'otherhead', b'otherchild'], None, [b'excluded']),
1095
1095
self.assertSeenAndResult(expected, search, search.__next__)
1096
1096
# using next_with_ghosts:
1097
1097
search = graph._make_breadth_first_searcher([])
1098
search.start_searching(['head'])
1098
search.start_searching([b'head'])
1099
1099
self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1101
1101
def test_breadth_first_stop_searching_not_queried(self):
1102
# A client should be able to say 'stop node X' even if X has not been
1102
# A client should be able to say b'stop node X' even if X has not been
1103
1103
# returned to the client.
1104
1104
graph = self.make_graph({
1105
'head': ['child', 'ghost1'],
1106
'child': [NULL_REVISION],
1105
b'head': [b'child', b'ghost1'],
1106
b'child': [NULL_REVISION],
1107
1107
NULL_REVISION: [],
1109
search = graph._make_breadth_first_searcher(['head'])
1109
search = graph._make_breadth_first_searcher([b'head'])
1111
1111
# NULL_REVISION and ghost1 have not been returned
1113
({'head'}, {'child', NULL_REVISION, 'ghost1'}, 1),
1114
['head'], None, [NULL_REVISION, 'ghost1']),
1113
({b'head'}, {b'child', NULL_REVISION, b'ghost1'}, 1),
1114
[b'head'], None, [NULL_REVISION, b'ghost1']),
1115
1115
# ghost1 has been returned, NULL_REVISION is to be returned in the
1116
1116
# next iteration.
1117
({'head', 'child', 'ghost1'},
1118
({'head'}, {'ghost1', NULL_REVISION}, 2),
1119
['head', 'child'], None, [NULL_REVISION, 'ghost1']),
1117
({b'head', b'child', b'ghost1'},
1118
({b'head'}, {b'ghost1', NULL_REVISION}, 2),
1119
[b'head', b'child'], None, [NULL_REVISION, b'ghost1']),
1121
1121
self.assertSeenAndResult(expected, search, search.__next__)
1122
1122
# using next_with_ghosts:
1123
search = graph._make_breadth_first_searcher(['head'])
1123
search = graph._make_breadth_first_searcher([b'head'])
1124
1124
self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1126
1126
def test_breadth_first_stop_searching_late(self):
1127
# A client should be able to say 'stop node X' and have it excluded
1127
# A client should be able to say b'stop node X' and have it excluded
1128
1128
# from the result even if X was seen in an older iteration of the
1130
1130
graph = self.make_graph({
1132
'middle': ['child'],
1133
'child': [NULL_REVISION],
1131
b'head': [b'middle'],
1132
b'middle': [b'child'],
1133
b'child': [NULL_REVISION],
1134
1134
NULL_REVISION: [],
1136
search = graph._make_breadth_first_searcher(['head'])
1136
search = graph._make_breadth_first_searcher([b'head'])
1138
({'head'}, ({'head'}, {'middle'}, 1),
1139
['head'], None, None),
1140
({'head', 'middle'}, ({'head'}, {'child'}, 2),
1141
['head', 'middle'], None, None),
1142
# 'middle' came from the previous iteration, but we don't stop
1138
({b'head'}, ({b'head'}, {b'middle'}, 1),
1139
[b'head'], None, None),
1140
({b'head', b'middle'}, ({b'head'}, {b'child'}, 2),
1141
[b'head', b'middle'], None, None),
1142
# b'middle' came from the previous iteration, but we don't stop
1143
1143
# searching it until *after* advancing the searcher.
1144
({'head', 'middle', 'child'},
1145
({'head'}, {'middle', 'child'}, 1),
1146
['head'], None, ['middle', 'child']),
1144
({b'head', b'middle', b'child'},
1145
({b'head'}, {b'middle', b'child'}, 1),
1146
[b'head'], None, [b'middle', b'child']),
1148
1148
self.assertSeenAndResult(expected, search, search.__next__)
1149
1149
# using next_with_ghosts:
1150
search = graph._make_breadth_first_searcher(['head'])
1150
search = graph._make_breadth_first_searcher([b'head'])
1151
1151
self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1153
1153
def test_breadth_first_get_result_ghosts_are_excluded(self):
1154
1154
graph = self.make_graph({
1155
'head': ['child', 'ghost'],
1156
'child': [NULL_REVISION],
1155
b'head': [b'child', b'ghost'],
1156
b'child': [NULL_REVISION],
1157
1157
NULL_REVISION: [],
1159
search = graph._make_breadth_first_searcher(['head'])
1159
search = graph._make_breadth_first_searcher([b'head'])
1163
({'head'}, {'ghost', 'child'}, 1),
1164
['head'], None, None),
1165
({'head', 'child', 'ghost'},
1166
({'head'}, {NULL_REVISION, 'ghost'}, 2),
1167
['head', 'child'], None, None),
1163
({b'head'}, {b'ghost', b'child'}, 1),
1164
[b'head'], None, None),
1165
({b'head', b'child', b'ghost'},
1166
({b'head'}, {NULL_REVISION, b'ghost'}, 2),
1167
[b'head', b'child'], None, None),
1169
1169
self.assertSeenAndResult(expected, search, search.__next__)
1170
1170
# using next_with_ghosts:
1171
search = graph._make_breadth_first_searcher(['head'])
1171
search = graph._make_breadth_first_searcher([b'head'])
1172
1172
self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1174
1174
def test_breadth_first_get_result_starting_a_ghost_ghost_is_excluded(self):
1175
1175
graph = self.make_graph({
1177
'child': [NULL_REVISION],
1176
b'head': [b'child'],
1177
b'child': [NULL_REVISION],
1178
1178
NULL_REVISION: [],
1180
search = graph._make_breadth_first_searcher(['head'])
1180
search = graph._make_breadth_first_searcher([b'head'])
1184
({'head', 'ghost'}, {'child', 'ghost'}, 1),
1185
['head'], ['ghost'], None),
1186
({'head', 'child', 'ghost'},
1187
({'head', 'ghost'}, {NULL_REVISION, 'ghost'}, 2),
1188
['head', 'child'], None, None),
1183
({b'head', b'ghost'},
1184
({b'head', b'ghost'}, {b'child', b'ghost'}, 1),
1185
[b'head'], [b'ghost'], None),
1186
({b'head', b'child', b'ghost'},
1187
({b'head', b'ghost'}, {NULL_REVISION, b'ghost'}, 2),
1188
[b'head', b'child'], None, None),
1190
1190
self.assertSeenAndResult(expected, search, search.__next__)
1191
1191
# using next_with_ghosts:
1192
search = graph._make_breadth_first_searcher(['head'])
1192
search = graph._make_breadth_first_searcher([b'head'])
1193
1193
self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1195
1195
def test_breadth_first_revision_count_includes_NULL_REVISION(self):
1196
1196
graph = self.make_graph({
1197
'head': [NULL_REVISION],
1197
b'head': [NULL_REVISION],
1198
1198
NULL_REVISION: [],
1200
search = graph._make_breadth_first_searcher(['head'])
1200
search = graph._make_breadth_first_searcher([b'head'])
1204
({'head'}, {NULL_REVISION}, 1),
1205
['head'], None, None),
1206
({'head', NULL_REVISION},
1207
({'head'}, set([]), 2),
1208
['head', NULL_REVISION], None, None),
1204
({b'head'}, {NULL_REVISION}, 1),
1205
[b'head'], None, None),
1206
({b'head', NULL_REVISION},
1207
({b'head'}, set([]), 2),
1208
[b'head', NULL_REVISION], None, None),
1210
1210
self.assertSeenAndResult(expected, search, search.__next__)
1211
1211
# using next_with_ghosts:
1212
search = graph._make_breadth_first_searcher(['head'])
1212
search = graph._make_breadth_first_searcher([b'head'])
1213
1213
self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1215
1215
def test_breadth_first_search_get_result_after_StopIteration(self):
1216
1216
# StopIteration should not invalid anything..
1217
1217
graph = self.make_graph({
1218
'head': [NULL_REVISION],
1218
b'head': [NULL_REVISION],
1219
1219
NULL_REVISION: [],
1221
search = graph._make_breadth_first_searcher(['head'])
1221
search = graph._make_breadth_first_searcher([b'head'])
1225
({'head'}, {NULL_REVISION}, 1),
1226
['head'], None, None),
1227
({'head', 'ghost', NULL_REVISION},
1228
({'head', 'ghost'}, {'ghost'}, 2),
1229
['head', NULL_REVISION], ['ghost'], None),
1225
({b'head'}, {NULL_REVISION}, 1),
1226
[b'head'], None, None),
1227
({b'head', b'ghost', NULL_REVISION},
1228
({b'head', b'ghost'}, {b'ghost'}, 2),
1229
[b'head', NULL_REVISION], [b'ghost'], None),
1231
1231
self.assertSeenAndResult(expected, search, search.__next__)
1232
1232
self.assertRaises(StopIteration, next, search)
1233
self.assertEqual({'head', 'ghost', NULL_REVISION}, search.seen)
1233
self.assertEqual({b'head', b'ghost', NULL_REVISION}, search.seen)
1234
1234
state = search.get_state()
1235
1235
self.assertEqual(
1236
({'ghost', 'head'}, {'ghost'},
1237
{'head', NULL_REVISION}),
1236
({b'ghost', b'head'}, {b'ghost'},
1237
{b'head', NULL_REVISION}),
1239
1239
# using next_with_ghosts:
1240
search = graph._make_breadth_first_searcher(['head'])
1240
search = graph._make_breadth_first_searcher([b'head'])
1241
1241
self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1242
1242
self.assertRaises(StopIteration, next, search)
1243
self.assertEqual({'head', 'ghost', NULL_REVISION}, search.seen)
1243
self.assertEqual({b'head', b'ghost', NULL_REVISION}, search.seen)
1244
1244
state = search.get_state()
1245
1245
self.assertEqual(
1246
({'ghost', 'head'}, {'ghost'},
1247
{'head', NULL_REVISION}),
1246
({b'ghost', b'head'}, {b'ghost'},
1247
{b'head', NULL_REVISION}),
1257
1257
def test_empty_set(self):
1258
1258
graph = self.make_graph(ancestry_1)
1259
self.assertFindUniqueAncestors(graph, [], 'rev1', ['rev1'])
1260
self.assertFindUniqueAncestors(graph, [], 'rev2b', ['rev2b'])
1261
self.assertFindUniqueAncestors(graph, [], 'rev3', ['rev1', 'rev3'])
1259
self.assertFindUniqueAncestors(graph, [], b'rev1', [b'rev1'])
1260
self.assertFindUniqueAncestors(graph, [], b'rev2b', [b'rev2b'])
1261
self.assertFindUniqueAncestors(graph, [], b'rev3', [b'rev1', b'rev3'])
1263
1263
def test_single_node(self):
1264
1264
graph = self.make_graph(ancestry_1)
1265
self.assertFindUniqueAncestors(graph, ['rev2a'], 'rev2a', ['rev1'])
1266
self.assertFindUniqueAncestors(graph, ['rev2b'], 'rev2b', ['rev1'])
1267
self.assertFindUniqueAncestors(graph, ['rev3'], 'rev3', ['rev2a'])
1265
self.assertFindUniqueAncestors(graph, [b'rev2a'], b'rev2a', [b'rev1'])
1266
self.assertFindUniqueAncestors(graph, [b'rev2b'], b'rev2b', [b'rev1'])
1267
self.assertFindUniqueAncestors(graph, [b'rev3'], b'rev3', [b'rev2a'])
1269
1269
def test_minimal_ancestry(self):
1270
1270
graph = self.make_breaking_graph(extended_history_shortcut,
1271
[NULL_REVISION, 'a', 'b'])
1272
self.assertFindUniqueAncestors(graph, ['e'], 'e', ['d'])
1271
[NULL_REVISION, b'a', b'b'])
1272
self.assertFindUniqueAncestors(graph, [b'e'], b'e', [b'd'])
1274
1274
graph = self.make_breaking_graph(extended_history_shortcut,
1276
self.assertFindUniqueAncestors(graph, ['f'], 'f', ['a', 'd'])
1276
self.assertFindUniqueAncestors(graph, [b'f'], b'f', [b'a', b'd'])
1278
1278
graph = self.make_breaking_graph(complex_shortcut,
1280
self.assertFindUniqueAncestors(graph, ['h'], 'h', ['i'])
1281
self.assertFindUniqueAncestors(graph, ['e', 'g', 'i'], 'i', ['h'])
1282
self.assertFindUniqueAncestors(graph, ['h'], 'h', ['g'])
1283
self.assertFindUniqueAncestors(graph, ['h'], 'h', ['j'])
1280
self.assertFindUniqueAncestors(graph, [b'h'], b'h', [b'i'])
1281
self.assertFindUniqueAncestors(graph, [b'e', b'g', b'i'], b'i', [b'h'])
1282
self.assertFindUniqueAncestors(graph, [b'h'], b'h', [b'g'])
1283
self.assertFindUniqueAncestors(graph, [b'h'], b'h', [b'j'])
1285
1285
def test_in_ancestry(self):
1286
1286
graph = self.make_graph(ancestry_1)
1287
self.assertFindUniqueAncestors(graph, [], 'rev1', ['rev3'])
1288
self.assertFindUniqueAncestors(graph, [], 'rev2b', ['rev4'])
1287
self.assertFindUniqueAncestors(graph, [], b'rev1', [b'rev3'])
1288
self.assertFindUniqueAncestors(graph, [], b'rev2b', [b'rev4'])
1290
1290
def test_multiple_revisions(self):
1291
1291
graph = self.make_graph(ancestry_1)
1292
1292
self.assertFindUniqueAncestors(graph,
1293
['rev4'], 'rev4', ['rev3', 'rev2b'])
1293
[b'rev4'], b'rev4', [b'rev3', b'rev2b'])
1294
1294
self.assertFindUniqueAncestors(graph,
1295
['rev2a', 'rev3', 'rev4'], 'rev4', ['rev2b'])
1295
[b'rev2a', b'rev3', b'rev4'], b'rev4', [b'rev2b'])
1297
1297
def test_complex_shortcut(self):
1298
1298
graph = self.make_graph(complex_shortcut)
1299
1299
self.assertFindUniqueAncestors(graph,
1300
['h', 'n'], 'n', ['m'])
1300
[b'h', b'n'], b'n', [b'm'])
1301
1301
self.assertFindUniqueAncestors(graph,
1302
['e', 'i', 'm'], 'm', ['n'])
1302
[b'e', b'i', b'm'], b'm', [b'n'])
1304
1304
def test_complex_shortcut2(self):
1305
1305
graph = self.make_graph(complex_shortcut2)
1306
1306
self.assertFindUniqueAncestors(graph,
1307
['j', 'u'], 'u', ['t'])
1307
[b'j', b'u'], b'u', [b't'])
1308
1308
self.assertFindUniqueAncestors(graph,
1309
[b't'], b't', [b'u'])
1311
1311
def test_multiple_interesting_unique(self):
1312
1312
graph = self.make_graph(multiple_interesting_unique)
1313
1313
self.assertFindUniqueAncestors(graph,
1314
['j', 'y'], 'y', ['z'])
1314
[b'j', b'y'], b'y', [b'z'])
1315
1315
self.assertFindUniqueAncestors(graph,
1316
['p', 'z'], 'z', ['y'])
1316
[b'p', b'z'], b'z', [b'y'])
1318
1318
def test_racing_shortcuts(self):
1319
1319
graph = self.make_graph(racing_shortcuts)
1320
1320
self.assertFindUniqueAncestors(graph,
1321
['p', 'q', 'z'], 'z', ['y'])
1321
[b'p', b'q', b'z'], b'z', [b'y'])
1322
1322
self.assertFindUniqueAncestors(graph,
1323
['h', 'i', 'j', 'y'], 'j', ['z'])
1323
[b'h', b'i', b'j', b'y'], b'j', [b'z'])
1326
1326
class TestGraphFindDistanceToNull(TestGraphBase):
1334
1334
def test_nothing_known(self):
1335
1335
graph = self.make_graph(ancestry_1)
1336
1336
self.assertFindDistance(0, graph, NULL_REVISION, [])
1337
self.assertFindDistance(1, graph, 'rev1', [])
1338
self.assertFindDistance(2, graph, 'rev2a', [])
1339
self.assertFindDistance(2, graph, 'rev2b', [])
1340
self.assertFindDistance(3, graph, 'rev3', [])
1341
self.assertFindDistance(4, graph, 'rev4', [])
1337
self.assertFindDistance(1, graph, b'rev1', [])
1338
self.assertFindDistance(2, graph, b'rev2a', [])
1339
self.assertFindDistance(2, graph, b'rev2b', [])
1340
self.assertFindDistance(3, graph, b'rev3', [])
1341
self.assertFindDistance(4, graph, b'rev4', [])
1343
1343
def test_rev_is_ghost(self):
1344
1344
graph = self.make_graph(ancestry_1)
1345
1345
e = self.assertRaises(errors.GhostRevisionsHaveNoRevno,
1346
graph.find_distance_to_null, 'rev_missing', [])
1347
self.assertEqual('rev_missing', e.revision_id)
1348
self.assertEqual('rev_missing', e.ghost_revision_id)
1346
graph.find_distance_to_null, b'rev_missing', [])
1347
self.assertEqual(b'rev_missing', e.revision_id)
1348
self.assertEqual(b'rev_missing', e.ghost_revision_id)
1350
1350
def test_ancestor_is_ghost(self):
1351
graph = self.make_graph({'rev':['parent']})
1351
graph = self.make_graph({b'rev': [b'parent']})
1352
1352
e = self.assertRaises(errors.GhostRevisionsHaveNoRevno,
1353
graph.find_distance_to_null, 'rev', [])
1354
self.assertEqual('rev', e.revision_id)
1355
self.assertEqual('parent', e.ghost_revision_id)
1353
graph.find_distance_to_null, b'rev', [])
1354
self.assertEqual(b'rev', e.revision_id)
1355
self.assertEqual(b'parent', e.ghost_revision_id)
1357
1357
def test_known_in_ancestry(self):
1358
1358
graph = self.make_graph(ancestry_1)
1359
self.assertFindDistance(2, graph, 'rev2a', [('rev1', 1)])
1360
self.assertFindDistance(3, graph, 'rev3', [('rev2a', 2)])
1359
self.assertFindDistance(2, graph, b'rev2a', [(b'rev1', 1)])
1360
self.assertFindDistance(3, graph, b'rev3', [(b'rev2a', 2)])
1362
1362
def test_known_in_ancestry_limits(self):
1363
graph = self.make_breaking_graph(ancestry_1, ['rev1'])
1364
self.assertFindDistance(4, graph, 'rev4', [('rev3', 3)])
1363
graph = self.make_breaking_graph(ancestry_1, [b'rev1'])
1364
self.assertFindDistance(4, graph, b'rev4', [(b'rev3', 3)])
1366
1366
def test_target_is_ancestor(self):
1367
1367
graph = self.make_graph(ancestry_1)
1368
self.assertFindDistance(2, graph, 'rev2a', [('rev3', 3)])
1368
self.assertFindDistance(2, graph, b'rev2a', [(b'rev3', 3)])
1370
1370
def test_target_is_ancestor_limits(self):
1371
1371
"""We shouldn't search all history if we run into ourselves"""
1372
graph = self.make_breaking_graph(ancestry_1, ['rev1'])
1373
self.assertFindDistance(3, graph, 'rev3', [('rev4', 4)])
1372
graph = self.make_breaking_graph(ancestry_1, [b'rev1'])
1373
self.assertFindDistance(3, graph, b'rev3', [(b'rev4', 4)])
1375
1375
def test_target_parallel_to_known_limits(self):
1376
1376
# Even though the known revision isn't part of the other ancestry, they
1377
1377
# eventually converge
1378
graph = self.make_breaking_graph(with_tail, ['a'])
1379
self.assertFindDistance(6, graph, 'f', [('g', 6)])
1380
self.assertFindDistance(7, graph, 'h', [('g', 6)])
1381
self.assertFindDistance(8, graph, 'i', [('g', 6)])
1382
self.assertFindDistance(6, graph, 'g', [('i', 8)])
1378
graph = self.make_breaking_graph(with_tail, [b'a'])
1379
self.assertFindDistance(6, graph, b'f', [(b'g', 6)])
1380
self.assertFindDistance(7, graph, b'h', [(b'g', 6)])
1381
self.assertFindDistance(8, graph, b'i', [(b'g', 6)])
1382
self.assertFindDistance(6, graph, b'g', [(b'i', 8)])
1385
1385
class TestFindMergeOrder(TestGraphBase):
1390
1390
def test_parents(self):
1391
1391
graph = self.make_graph(ancestry_1)
1392
self.assertMergeOrder(['rev3', 'rev2b'], graph, 'rev4',
1394
self.assertMergeOrder(['rev3', 'rev2b'], graph, 'rev4',
1392
self.assertMergeOrder([b'rev3', b'rev2b'], graph, b'rev4',
1393
[b'rev3', b'rev2b'])
1394
self.assertMergeOrder([b'rev3', b'rev2b'], graph, b'rev4',
1395
[b'rev2b', b'rev3'])
1397
1397
def test_ancestors(self):
1398
1398
graph = self.make_graph(ancestry_1)
1399
self.assertMergeOrder(['rev1', 'rev2b'], graph, 'rev4',
1401
self.assertMergeOrder(['rev1', 'rev2b'], graph, 'rev4',
1399
self.assertMergeOrder([b'rev1', b'rev2b'], graph, b'rev4',
1400
[b'rev1', b'rev2b'])
1401
self.assertMergeOrder([b'rev1', b'rev2b'], graph, b'rev4',
1402
[b'rev2b', b'rev1'])
1404
1404
def test_shortcut_one_ancestor(self):
1405
1405
# When we have enough info, we can stop searching
1406
graph = self.make_breaking_graph(ancestry_1, ['rev3', 'rev2b', 'rev4'])
1406
graph = self.make_breaking_graph(ancestry_1, [b'rev3', b'rev2b', b'rev4'])
1407
1407
# Single ancestors shortcut right away
1408
self.assertMergeOrder(['rev3'], graph, 'rev4', ['rev3'])
1408
self.assertMergeOrder([b'rev3'], graph, b'rev4', [b'rev3'])
1410
1410
def test_shortcut_after_one_ancestor(self):
1411
graph = self.make_breaking_graph(ancestry_1, ['rev2a', 'rev2b'])
1412
self.assertMergeOrder(['rev3', 'rev1'], graph, 'rev4', ['rev1', 'rev3'])
1411
graph = self.make_breaking_graph(ancestry_1, [b'rev2a', b'rev2b'])
1412
self.assertMergeOrder([b'rev3', b'rev1'], graph, b'rev4', [b'rev1', b'rev3'])
1415
1415
class TestFindDescendants(TestGraphBase):
1417
1417
def test_find_descendants_rev1_rev3(self):
1418
1418
graph = self.make_graph(ancestry_1)
1419
descendants = graph.find_descendants('rev1', 'rev3')
1420
self.assertEqual({'rev1', 'rev2a', 'rev3'}, descendants)
1419
descendants = graph.find_descendants(b'rev1', b'rev3')
1420
self.assertEqual({b'rev1', b'rev2a', b'rev3'}, descendants)
1422
1422
def test_find_descendants_rev1_rev4(self):
1423
1423
graph = self.make_graph(ancestry_1)
1424
descendants = graph.find_descendants('rev1', 'rev4')
1425
self.assertEqual({'rev1', 'rev2a', 'rev2b', 'rev3', 'rev4'},
1424
descendants = graph.find_descendants(b'rev1', b'rev4')
1425
self.assertEqual({b'rev1', b'rev2a', b'rev2b', b'rev3', b'rev4'},
1428
1428
def test_find_descendants_rev2a_rev4(self):
1429
1429
graph = self.make_graph(ancestry_1)
1430
descendants = graph.find_descendants('rev2a', 'rev4')
1431
self.assertEqual({'rev2a', 'rev3', 'rev4'}, descendants)
1430
descendants = graph.find_descendants(b'rev2a', b'rev4')
1431
self.assertEqual({b'rev2a', b'rev3', b'rev4'}, descendants)
1433
1433
class TestFindLefthandMerger(TestGraphBase):
1478
1478
def setUp(self):
1479
1479
super(TestCachingParentsProvider, self).setUp()
1480
dict_pp = _mod_graph.DictParentsProvider({'a': ('b',)})
1480
dict_pp = _mod_graph.DictParentsProvider({b'a': (b'b',)})
1481
1481
self.inst_pp = InstrumentedParentsProvider(dict_pp)
1482
1482
self.caching_pp = _mod_graph.CachingParentsProvider(self.inst_pp)
1484
1484
def test_get_parent_map(self):
1485
1485
"""Requesting the same revision should be returned from cache"""
1486
1486
self.assertEqual({}, self.caching_pp._cache)
1487
self.assertEqual({'a':('b',)}, self.caching_pp.get_parent_map(['a']))
1488
self.assertEqual(['a'], self.inst_pp.calls)
1489
self.assertEqual({'a':('b',)}, self.caching_pp.get_parent_map(['a']))
1487
self.assertEqual({b'a':(b'b',)}, self.caching_pp.get_parent_map([b'a']))
1488
self.assertEqual([b'a'], self.inst_pp.calls)
1489
self.assertEqual({b'a':(b'b',)}, self.caching_pp.get_parent_map([b'a']))
1490
1490
# No new call, as it should have been returned from the cache
1491
self.assertEqual(['a'], self.inst_pp.calls)
1492
self.assertEqual({'a':('b',)}, self.caching_pp._cache)
1491
self.assertEqual([b'a'], self.inst_pp.calls)
1492
self.assertEqual({b'a':(b'b',)}, self.caching_pp._cache)
1494
1494
def test_get_parent_map_not_present(self):
1495
1495
"""The cache should also track when a revision doesn't exist"""
1496
self.assertEqual({}, self.caching_pp.get_parent_map(['b']))
1497
self.assertEqual(['b'], self.inst_pp.calls)
1498
self.assertEqual({}, self.caching_pp.get_parent_map(['b']))
1496
self.assertEqual({}, self.caching_pp.get_parent_map([b'b']))
1497
self.assertEqual([b'b'], self.inst_pp.calls)
1498
self.assertEqual({}, self.caching_pp.get_parent_map([b'b']))
1500
self.assertEqual(['b'], self.inst_pp.calls)
1500
self.assertEqual([b'b'], self.inst_pp.calls)
1502
1502
def test_get_parent_map_mixed(self):
1503
1503
"""Anything that can be returned from cache, should be"""
1504
self.assertEqual({}, self.caching_pp.get_parent_map(['b']))
1505
self.assertEqual(['b'], self.inst_pp.calls)
1506
self.assertEqual({'a':('b',)},
1507
self.caching_pp.get_parent_map(['a', 'b']))
1508
self.assertEqual(['b', 'a'], self.inst_pp.calls)
1504
self.assertEqual({}, self.caching_pp.get_parent_map([b'b']))
1505
self.assertEqual([b'b'], self.inst_pp.calls)
1506
self.assertEqual({b'a':(b'b',)},
1507
self.caching_pp.get_parent_map([b'a', b'b']))
1508
self.assertEqual([b'b', b'a'], self.inst_pp.calls)
1510
1510
def test_get_parent_map_repeated(self):
1511
1511
"""Asking for the same parent 2x will only forward 1 request."""
1512
self.assertEqual({'a':('b',)},
1513
self.caching_pp.get_parent_map(['b', 'a', 'b']))
1512
self.assertEqual({b'a':(b'b',)},
1513
self.caching_pp.get_parent_map([b'b', b'a', b'b']))
1514
1514
# Use sorted because we don't care about the order, just that each is
1515
1515
# only present 1 time.
1516
self.assertEqual(['a', 'b'], sorted(self.inst_pp.calls))
1516
self.assertEqual([b'a', b'b'], sorted(self.inst_pp.calls))
1518
1518
def test_note_missing_key(self):
1519
1519
"""After noting that a key is missing it is cached."""
1520
self.caching_pp.note_missing_key('b')
1521
self.assertEqual({}, self.caching_pp.get_parent_map(['b']))
1520
self.caching_pp.note_missing_key(b'b')
1521
self.assertEqual({}, self.caching_pp.get_parent_map([b'b']))
1522
1522
self.assertEqual([], self.inst_pp.calls)
1523
self.assertEqual({'b'}, self.caching_pp.missing_keys)
1523
self.assertEqual({b'b'}, self.caching_pp.missing_keys)
1525
1525
def test_get_cached_parent_map(self):
1526
self.assertEqual({}, self.caching_pp.get_cached_parent_map(['a']))
1526
self.assertEqual({}, self.caching_pp.get_cached_parent_map([b'a']))
1527
1527
self.assertEqual([], self.inst_pp.calls)
1528
self.assertEqual({'a': ('b',)}, self.caching_pp.get_parent_map(['a']))
1529
self.assertEqual(['a'], self.inst_pp.calls)
1530
self.assertEqual({'a': ('b',)},
1531
self.caching_pp.get_cached_parent_map(['a']))
1528
self.assertEqual({b'a': (b'b',)}, self.caching_pp.get_parent_map([b'a']))
1529
self.assertEqual([b'a'], self.inst_pp.calls)
1530
self.assertEqual({b'a': (b'b',)},
1531
self.caching_pp.get_cached_parent_map([b'a']))
1534
1534
class TestCachingParentsProviderExtras(tests.TestCaseWithTransport):
1652
d = {('D',): [('B',), ('C',)], ('C',):[('A',)],
1653
('B',): [('A',)], ('A',): []}
1652
d = {(b'D',): [(b'B',), (b'C',)], (b'C',):[(b'A',)],
1653
(b'B',): [(b'A',)], (b'A',): []}
1654
1654
g = _mod_graph.Graph(_mod_graph.DictParentsProvider(d))
1655
1655
graph_thunk = _mod_graph.GraphThunkIdsToKeys(g)
1656
self.assertEqual(['D'], sorted(graph_thunk.heads(['D', 'A'])))
1657
self.assertEqual(['D'], sorted(graph_thunk.heads(['D', 'B'])))
1658
self.assertEqual(['D'], sorted(graph_thunk.heads(['D', 'C'])))
1659
self.assertEqual(['B', 'C'], sorted(graph_thunk.heads(['B', 'C'])))
1656
self.assertEqual([b'D'], sorted(graph_thunk.heads([b'D', b'A'])))
1657
self.assertEqual([b'D'], sorted(graph_thunk.heads([b'D', b'B'])))
1658
self.assertEqual([b'D'], sorted(graph_thunk.heads([b'D', b'C'])))
1659
self.assertEqual([b'B', b'C'], sorted(graph_thunk.heads([b'B', b'C'])))
1661
1661
def test_add_node(self):
1662
d = {('C',):[('A',)], ('B',): [('A',)], ('A',): []}
1662
d = {(b'C',):[(b'A',)], (b'B',): [(b'A',)], (b'A',): []}
1663
1663
g = _mod_graph.KnownGraph(d)
1664
1664
graph_thunk = _mod_graph.GraphThunkIdsToKeys(g)
1665
graph_thunk.add_node("D", ["A", "C"])
1666
self.assertEqual(['B', 'D'],
1667
sorted(graph_thunk.heads(['D', 'B', 'A'])))
1665
graph_thunk.add_node(b"D", [b"A", b"C"])
1666
self.assertEqual([b'B', b'D'],
1667
sorted(graph_thunk.heads([b'D', b'B', b'A'])))
1669
1669
def test_merge_sort(self):
1670
d = {('C',):[('A',)], ('B',): [('A',)], ('A',): []}
1670
d = {(b'C',):[(b'A',)], (b'B',): [(b'A',)], (b'A',): []}
1671
1671
g = _mod_graph.KnownGraph(d)
1672
1672
graph_thunk = _mod_graph.GraphThunkIdsToKeys(g)
1673
graph_thunk.add_node("D", ["A", "C"])
1674
self.assertEqual([('C', 0, (2,), False), ('A', 0, (1,), True)],
1673
graph_thunk.add_node(b"D", [b"A", b"C"])
1674
self.assertEqual([(b'C', 0, (2,), False), (b'A', 0, (1,), True)],
1675
1675
[(n.key, n.merge_depth, n.revno, n.end_of_merge)
1676
for n in graph_thunk.merge_sort('C')])
1676
for n in graph_thunk.merge_sort(b'C')])
1679
1679
class TestStackedParentsProvider(tests.TestCase):
1689
1689
return SharedInstrumentedParentsProvider(pp, self.calls, info)
1691
1691
def test_stacked_parents_provider(self):
1692
parents1 = _mod_graph.DictParentsProvider({'rev2': ['rev3']})
1693
parents2 = _mod_graph.DictParentsProvider({'rev1': ['rev4']})
1692
parents1 = _mod_graph.DictParentsProvider({b'rev2': [b'rev3']})
1693
parents2 = _mod_graph.DictParentsProvider({b'rev1': [b'rev4']})
1694
1694
stacked = _mod_graph.StackedParentsProvider([parents1, parents2])
1695
self.assertEqual({'rev1':['rev4'], 'rev2':['rev3']},
1696
stacked.get_parent_map(['rev1', 'rev2']))
1697
self.assertEqual({'rev2':['rev3'], 'rev1':['rev4']},
1698
stacked.get_parent_map(['rev2', 'rev1']))
1699
self.assertEqual({'rev2':['rev3']},
1700
stacked.get_parent_map(['rev2', 'rev2']))
1701
self.assertEqual({'rev1':['rev4']},
1702
stacked.get_parent_map(['rev1', 'rev1']))
1695
self.assertEqual({b'rev1':[b'rev4'], b'rev2':[b'rev3']},
1696
stacked.get_parent_map([b'rev1', b'rev2']))
1697
self.assertEqual({b'rev2':[b'rev3'], b'rev1':[b'rev4']},
1698
stacked.get_parent_map([b'rev2', b'rev1']))
1699
self.assertEqual({b'rev2':[b'rev3']},
1700
stacked.get_parent_map([b'rev2', b'rev2']))
1701
self.assertEqual({b'rev1':[b'rev4']},
1702
stacked.get_parent_map([b'rev1', b'rev1']))
1704
1704
def test_stacked_parents_provider_overlapping(self):
1705
1705
# rev2 is availible in both providers.
1709
parents1 = _mod_graph.DictParentsProvider({'rev2': ['rev1']})
1710
parents2 = _mod_graph.DictParentsProvider({'rev2': ['rev1']})
1709
parents1 = _mod_graph.DictParentsProvider({b'rev2': [b'rev1']})
1710
parents2 = _mod_graph.DictParentsProvider({b'rev2': [b'rev1']})
1711
1711
stacked = _mod_graph.StackedParentsProvider([parents1, parents2])
1712
self.assertEqual({'rev2': ['rev1']},
1713
stacked.get_parent_map(['rev2']))
1712
self.assertEqual({b'rev2': [b'rev1']},
1713
stacked.get_parent_map([b'rev2']))
1715
1715
def test_handles_no_get_cached_parent_map(self):
1716
1716
# this shows that we both handle when a provider doesn't implement
1717
1717
# get_cached_parent_map
1718
pp1 = self.get_shared_provider('pp1', {'rev2': ('rev1',)},
1718
pp1 = self.get_shared_provider(b'pp1', {b'rev2': (b'rev1',)},
1719
1719
has_cached=False)
1720
pp2 = self.get_shared_provider('pp2', {'rev2': ('rev1',)},
1720
pp2 = self.get_shared_provider(b'pp2', {b'rev2': (b'rev1',)},
1721
1721
has_cached=True)
1722
1722
stacked = _mod_graph.StackedParentsProvider([pp1, pp2])
1723
self.assertEqual({'rev2': ('rev1',)}, stacked.get_parent_map(['rev2']))
1724
# No call on 'pp1' because it doesn't provide get_cached_parent_map
1725
self.assertEqual([('pp2', 'cached', ['rev2'])], self.calls)
1723
self.assertEqual({b'rev2': (b'rev1',)}, stacked.get_parent_map([b'rev2']))
1724
# No call on b'pp1' because it doesn't provide get_cached_parent_map
1725
self.assertEqual([(b'pp2', 'cached', [b'rev2'])], self.calls)
1727
1727
def test_query_order(self):
1728
1728
# We should call get_cached_parent_map on all providers before we call
1729
1729
# get_parent_map. Further, we should track what entries we have found,
1730
1730
# and not re-try them.
1731
pp1 = self.get_shared_provider('pp1', {'a': ()}, has_cached=True)
1732
pp2 = self.get_shared_provider('pp2', {'c': ('b',)}, has_cached=False)
1733
pp3 = self.get_shared_provider('pp3', {'b': ('a',)}, has_cached=True)
1731
pp1 = self.get_shared_provider(b'pp1', {b'a': ()}, has_cached=True)
1732
pp2 = self.get_shared_provider(b'pp2', {b'c': (b'b',)}, has_cached=False)
1733
pp3 = self.get_shared_provider(b'pp3', {b'b': (b'a',)}, has_cached=True)
1734
1734
stacked = _mod_graph.StackedParentsProvider([pp1, pp2, pp3])
1735
self.assertEqual({'a': (), 'b': ('a',), 'c': ('b',)},
1736
stacked.get_parent_map(['a', 'b', 'c', 'd']))
1737
self.assertEqual([('pp1', 'cached', ['a', 'b', 'c', 'd']),
1735
self.assertEqual({b'a': (), b'b': (b'a',), b'c': (b'b',)},
1736
stacked.get_parent_map([b'a', b'b', b'c', b'd']))
1737
self.assertEqual([(b'pp1', 'cached', [b'a', b'b', b'c', b'd']),
1738
1738
# No call to pp2, because it doesn't have cached
1739
('pp3', 'cached', ['b', 'c', 'd']),
1740
('pp1', ['c', 'd']),
1741
('pp2', ['c', 'd']),
1739
(b'pp3', 'cached', [b'b', b'c', b'd']),
1740
(b'pp1', [b'c', b'd']),
1741
(b'pp2', [b'c', b'd']),