/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_graph.py

  • Committer: Jelmer Vernooij
  • Date: 2017-07-23 22:06:41 UTC
  • mfrom: (6738 trunk)
  • mto: This revision was merged to the branch mainline in revision 6739.
  • Revision ID: jelmer@jelmer.uk-20170723220641-69eczax9bmv8d6kk
Merge trunk, address review comments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
#   rev3  /
35
35
#     |  /
36
36
#   rev4
37
 
ancestry_1 = {b'rev1': [NULL_REVISION], b'rev2a': [b'rev1'], b'rev2b': [b'rev1'],
38
 
              b'rev3': [b'rev2a'], b'rev4': [b'rev3', b'rev2b']}
 
37
ancestry_1 = {'rev1': [NULL_REVISION], 'rev2a': ['rev1'], 'rev2b': ['rev1'],
 
38
              'rev3': ['rev2a'], 'rev4': ['rev3', 'rev2b']}
39
39
 
40
40
 
41
41
# Ancestry 2:
49
49
# rev3a
50
50
#   |
51
51
# rev4a
52
 
ancestry_2 = {b'rev1a': [NULL_REVISION], b'rev2a': [b'rev1a'],
53
 
              b'rev1b': [NULL_REVISION], b'rev3a': [b'rev2a'], b'rev4a': [b'rev3a']}
 
52
ancestry_2 = {'rev1a': [NULL_REVISION], 'rev2a': ['rev1a'],
 
53
              'rev1b': [NULL_REVISION], 'rev3a': ['rev2a'], 'rev4a': ['rev3a']}
54
54
 
55
55
 
56
56
# Criss cross ancestry
64
64
#       |  X |
65
65
#       |/  \|
66
66
#    rev3a  rev3b
67
 
criss_cross = {b'rev1': [NULL_REVISION], b'rev2a': [b'rev1'], b'rev2b': [b'rev1'],
68
 
               b'rev3a': [b'rev2a', b'rev2b'], b'rev3b': [b'rev2b', b'rev2a']}
 
67
criss_cross = {'rev1': [NULL_REVISION], 'rev2a': ['rev1'], 'rev2b': ['rev1'],
 
68
               'rev3a': ['rev2a', 'rev2b'], 'rev3b': ['rev2b', 'rev2a']}
69
69
 
70
70
 
71
71
# Criss-cross 2
79
79
#   | / \ |
80
80
#   |/   \|
81
81
# rev2a  rev2b
82
 
criss_cross2 = {b'rev1a': [NULL_REVISION], b'rev1b': [NULL_REVISION],
83
 
                b'rev2a': [b'rev1a', b'rev1b'], b'rev2b': [b'rev1b', b'rev1a']}
 
82
criss_cross2 = {'rev1a': [NULL_REVISION], 'rev1b': [NULL_REVISION],
 
83
                'rev2a': ['rev1a', 'rev1b'], 'rev2b': ['rev1b', 'rev1a']}
84
84
 
85
85
 
86
86
# Mainline:
92
92
#      | rev2b
93
93
#      |  /
94
94
#     rev2a
95
 
mainline = {b'rev1': [NULL_REVISION], b'rev2a': [b'rev1', b'rev2b'],
96
 
            b'rev2b': [b'rev1']}
 
95
mainline = {'rev1': [NULL_REVISION], 'rev2a': ['rev1', 'rev2b'],
 
96
            'rev2b': ['rev1']}
97
97
 
98
98
 
99
99
# feature branch:
105
105
#     rev2b
106
106
#       |
107
107
#     rev3b
108
 
feature_branch = {b'rev1': [NULL_REVISION],
109
 
                  b'rev2b': [b'rev1'], b'rev3b': [b'rev2b']}
 
108
feature_branch = {'rev1': [NULL_REVISION],
 
109
                  'rev2b': ['rev1'], 'rev3b': ['rev2b']}
110
110
 
111
111
 
112
112
# History shortcut
117
117
#  rev2a rev2b rev2c
118
118
#    |  /   \   /
119
119
#  rev3a    rev3b
120
 
history_shortcut = {b'rev1': [NULL_REVISION], b'rev2a': [b'rev1'],
121
 
                    b'rev2b': [b'rev1'], b'rev2c': [b'rev1'],
122
 
                    b'rev3a': [b'rev2a', b'rev2b'], b'rev3b': [b'rev2b', b'rev2c']}
 
120
history_shortcut = {'rev1': [NULL_REVISION], 'rev2a': ['rev1'],
 
121
                    'rev2b': ['rev1'], 'rev2c': ['rev1'],
 
122
                    'rev3a': ['rev2a', 'rev2b'], 'rev3b': ['rev2b', 'rev2c']}
123
123
 
124
124
# Extended history shortcut
125
125
#  NULL_REVISION
133
133
#       d |
134
134
#       |\|
135
135
#       e f
136
 
extended_history_shortcut = {b'a': [NULL_REVISION],
137
 
                             b'b': [b'a'],
138
 
                             b'c': [b'b'],
139
 
                             b'd': [b'c'],
140
 
                             b'e': [b'd'],
141
 
                             b'f': [b'a', b'd'],
142
 
                             }
 
136
extended_history_shortcut = {'a': [NULL_REVISION],
 
137
                             'b': ['a'],
 
138
                             'c': ['b'],
 
139
                             'd': ['c'],
 
140
                             'e': ['d'],
 
141
                             'f': ['a', 'd'],
 
142
                            }
143
143
 
144
144
# Double shortcut
145
 
# Both sides will see b'A' first, even though it is actually a decendent of a
 
145
# Both sides will see 'A' first, even though it is actually a decendent of a
146
146
# different common revision.
147
147
#
148
148
#  NULL_REVISION
157
157
#   |/     \|
158
158
#   f       g
159
159
 
160
 
double_shortcut = {b'a': [NULL_REVISION], b'b': [b'a'], b'c': [b'b'],
161
 
                   b'd': [b'c'], b'e': [b'c'], b'f': [b'a', b'd'],
162
 
                   b'g': [b'a', b'e']}
 
160
double_shortcut = {'a':[NULL_REVISION], 'b':['a'], 'c':['b'],
 
161
                   'd':['c'], 'e':['c'], 'f':['a', 'd'],
 
162
                   'g':['a', 'e']}
163
163
 
164
164
# Complex shortcut
165
165
# This has a failure mode in that a shortcut will find some nodes in common,
189
189
#     | l |
190
190
#     |/|/
191
191
#     m n
192
 
complex_shortcut = {b'a': [NULL_REVISION], b'b': [b'a'], b'c': [b'b'], b'd': [b'c'],
193
 
                    b'e': [b'd'], b'f': [b'd'], b'g': [b'f'], b'h': [b'f'],
194
 
                    b'i': [b'e', b'g'], b'j': [b'g'], b'k': [b'j'],
195
 
                    b'l': [b'k'], b'm': [b'i', b'l'], b'n': [b'l', b'h']}
 
192
complex_shortcut = {'a':[NULL_REVISION], 'b':['a'], 'c':['b'], 'd':['c'],
 
193
                    'e':['d'], 'f':['d'], 'g':['f'], 'h':['f'],
 
194
                    'i':['e', 'g'], 'j':['g'], 'k':['j'],
 
195
                    'l':['k'], 'm':['i', 'l'], 'n':['l', 'h']}
196
196
 
197
197
# NULL_REVISION
198
198
#     |
232
232
#     | | |
233
233
#     |/|/
234
234
#     t u
235
 
complex_shortcut2 = {b'a': [NULL_REVISION], b'b': [b'a'], b'c': [b'b'], b'd': [b'c'],
236
 
                     b'e': [b'd'], b'f': [b'e'], b'g': [b'f'], b'h': [b'd'], b'i': [b'g'],
237
 
                     b'j': [b'h'], b'k': [b'h', b'i'], b'l': [b'k'], b'm': [b'l'], b'n': [b'm'],
238
 
                     b'o': [b'n'], b'p': [b'o'], b'q': [b'p'], b'r': [b'q'], b's': [b'r'],
239
 
                     b't': [b'i', b's'], b'u': [b's', b'j'],
240
 
                     }
 
235
complex_shortcut2 = {'a':[NULL_REVISION], 'b':['a'], 'c':['b'], 'd':['c'],
 
236
                    'e':['d'], 'f':['e'], 'g':['f'], 'h':['d'], 'i':['g'],
 
237
                    'j':['h'], 'k':['h', 'i'], 'l':['k'], 'm':['l'], 'n':['m'],
 
238
                    'o':['n'], 'p':['o'], 'q':['p'], 'r':['q'], 's':['r'],
 
239
                    't':['i', 's'], 'u':['s', 'j'],
 
240
                    }
241
241
 
242
242
# Graph where different walkers will race to find the common and uncommon
243
243
# nodes.
290
290
# k-n exists so that the second pass still has nodes that are worth searching,
291
291
# rather than instantly cancelling the extra walker.
292
292
 
293
 
racing_shortcuts = {b'a': [NULL_REVISION], b'b': [b'a'], b'c': [b'b'], b'd': [b'c'],
294
 
                    b'e': [b'd'], b'f': [b'e'], b'g': [b'f'], b'h': [b'g'], b'i': [b'h', b'o'], b'j': [b'i', b'y'],
295
 
                    b'k': [b'd'], b'l': [b'k'], b'm': [b'l'], b'n': [b'm'], b'o': [b'n', b'g'], b'p': [b'f'],
296
 
                    b'q': [b'p', b'm'], b'r': [b'o'], b's': [b'r'], b't': [b's'], b'u': [b't'], b'v': [b'u'],
297
 
                    b'w': [b'v'], b'x': [b'w'], b'y': [b'x'], b'z': [b'x', b'q']}
 
293
racing_shortcuts = {'a':[NULL_REVISION], 'b':['a'], 'c':['b'], 'd':['c'],
 
294
    'e':['d'], 'f':['e'], 'g':['f'], 'h':['g'], 'i':['h', 'o'], 'j':['i', 'y'],
 
295
    'k':['d'], 'l':['k'], 'm':['l'], 'n':['m'], 'o':['n', 'g'], 'p':['f'],
 
296
    'q':['p', 'm'], 'r':['o'], 's':['r'], 't':['s'], 'u':['t'], 'v':['u'],
 
297
    'w':['v'], 'x':['w'], 'y':['x'], 'z':['x', 'q']}
298
298
 
299
299
 
300
300
# A graph with multiple nodes unique to one side.
336
336
#     y   z
337
337
#
338
338
 
339
 
multiple_interesting_unique = {b'a': [NULL_REVISION], b'b': [b'a'], b'c': [b'b'],
340
 
                               b'd': [b'c'], b'e': [b'd'], b'f': [b'd'], b'g': [b'e'], b'h': [b'e'], b'i': [b'f'],
341
 
                               b'j': [b'g'], b'k': [b'g'], b'l': [b'h'], b'm': [b'i'], b'n': [b'k', b'l'],
342
 
                               b'o': [b'm'], b'p': [b'm', b'l'], b'q': [b'n', b'o'], b'r': [b'q'], b's': [b'r'],
343
 
                               b't': [b's'], b'u': [b't'], b'v': [b'u'], b'w': [b'v'], b'x': [b'w'],
344
 
                               b'y': [b'j', b'x'], b'z': [b'x', b'p']}
 
339
multiple_interesting_unique = {'a':[NULL_REVISION], 'b':['a'], 'c':['b'],
 
340
    'd':['c'], 'e':['d'], 'f':['d'], 'g':['e'], 'h':['e'], 'i':['f'],
 
341
    'j':['g'], 'k':['g'], 'l':['h'], 'm':['i'], 'n':['k', 'l'],
 
342
    'o':['m'], 'p':['m', 'l'], 'q':['n', 'o'], 'r':['q'], 's':['r'],
 
343
    't':['s'], 'u':['t'], 'v':['u'], 'w':['v'], 'x':['w'],
 
344
    'y':['j', 'x'], 'z':['x', 'p']}
345
345
 
346
346
 
347
347
# Shortcut with extra root
358
358
#       d | g
359
359
#       |\|/
360
360
#       e f
361
 
shortcut_extra_root = {b'a': [NULL_REVISION],
362
 
                       b'b': [b'a'],
363
 
                       b'c': [b'b'],
364
 
                       b'd': [b'c'],
365
 
                       b'e': [b'd'],
366
 
                       b'f': [b'a', b'd', b'g'],
367
 
                       b'g': [NULL_REVISION],
368
 
                       }
 
361
shortcut_extra_root = {'a': [NULL_REVISION],
 
362
                       'b': ['a'],
 
363
                       'c': ['b'],
 
364
                       'd': ['c'],
 
365
                       'e': ['d'],
 
366
                       'f': ['a', 'd', 'g'],
 
367
                       'g': [NULL_REVISION],
 
368
                      }
369
369
 
370
370
#  NULL_REVISION
371
371
#       |
377
377
#     | \ |
378
378
#     a   c
379
379
 
380
 
boundary = {b'a': [b'b'], b'c': [b'b', b'd'], b'b': [b'e'], b'd': [b'e'], b'e': [b'f'],
381
 
            b'f': [NULL_REVISION]}
 
380
boundary = {'a': ['b'], 'c': ['b', 'd'], 'b':['e'], 'd':['e'], 'e': ['f'],
 
381
            'f':[NULL_REVISION]}
382
382
 
383
383
 
384
384
# A graph that contains a ghost
392
392
#     | \ |
393
393
#     a   c
394
394
 
395
 
with_ghost = {b'a': [b'b'], b'c': [b'b', b'd'], b'b': [b'e'], b'd': [b'e', b'g'],
396
 
              b'e': [b'f'], b'f': [NULL_REVISION], NULL_REVISION: ()}
 
395
with_ghost = {'a': ['b'], 'c': ['b', 'd'], 'b':['e'], 'd':['e', 'g'],
 
396
              'e': ['f'], 'f':[NULL_REVISION], NULL_REVISION:()}
397
397
 
398
398
# A graph that shows we can shortcut finding revnos when reaching them from the
399
399
# side.
415
415
#     |
416
416
#     i
417
417
 
418
 
with_tail = {b'a': [NULL_REVISION], b'b': [b'a'], b'c': [b'b'], b'd': [b'c'], b'e': [b'd'],
419
 
             b'f': [b'e'], b'g': [b'e'], b'h': [b'f'], b'i': [b'h']}
 
418
with_tail = {'a':[NULL_REVISION], 'b':['a'], 'c':['b'], 'd':['c'], 'e':['d'],
 
419
             'f':['e'], 'g':['e'], 'h':['f'], 'i':['h']}
420
420
 
421
421
 
422
422
class InstrumentedParentsProvider(object):
469
469
        """Make a Graph that raises an exception if we hit a node."""
470
470
        g = self.make_graph(ancestors)
471
471
        orig_parent_map = g.get_parent_map
472
 
 
473
472
        def get_parent_map(keys):
474
473
            bad_keys = set(keys).intersection(break_on)
475
474
            if bad_keys:
509
508
                parents = [p for p in ancestors[descendant] if p is not
510
509
                           NULL_REVISION]
511
510
                if len([p for p in parents if not
512
 
                        tree.branch.repository.has_revision(p)]) > 0:
 
511
                    tree.branch.repository.has_revision(p)]) > 0:
513
512
                    continue
514
513
                tree.set_parent_ids(parents)
515
514
                if len(parents) > 0:
532
531
        self.assertEqual({NULL_REVISION},
533
532
                         graph.find_lca(NULL_REVISION, NULL_REVISION))
534
533
        self.assertEqual({NULL_REVISION},
535
 
                         graph.find_lca(NULL_REVISION, b'rev1'))
536
 
        self.assertEqual({b'rev1'}, graph.find_lca(b'rev1', b'rev1'))
537
 
        self.assertEqual({b'rev1'}, graph.find_lca(b'rev2a', b'rev2b'))
 
534
                         graph.find_lca(NULL_REVISION, 'rev1'))
 
535
        self.assertEqual({'rev1'}, graph.find_lca('rev1', 'rev1'))
 
536
        self.assertEqual({'rev1'}, graph.find_lca('rev2a', 'rev2b'))
538
537
 
539
538
    def test_no_unique_lca(self):
540
539
        """Test error when one revision is not in the graph"""
541
540
        graph = self.make_graph(ancestry_1)
542
541
        self.assertRaises(errors.NoCommonAncestor, graph.find_unique_lca,
543
 
                          b'rev1', b'1rev')
 
542
                          'rev1', '1rev')
544
543
 
545
544
    def test_lca_criss_cross(self):
546
545
        """Test least-common-ancestor after a criss-cross merge."""
547
546
        graph = self.make_graph(criss_cross)
548
 
        self.assertEqual({b'rev2a', b'rev2b'},
549
 
                         graph.find_lca(b'rev3a', b'rev3b'))
550
 
        self.assertEqual({b'rev2b'},
551
 
                         graph.find_lca(b'rev3a', b'rev3b', b'rev2b'))
 
547
        self.assertEqual({'rev2a', 'rev2b'},
 
548
                         graph.find_lca('rev3a', 'rev3b'))
 
549
        self.assertEqual({'rev2b'},
 
550
                         graph.find_lca('rev3a', 'rev3b', 'rev2b'))
552
551
 
553
552
    def test_lca_shortcut(self):
554
553
        """Test least-common ancestor on this history shortcut"""
555
554
        graph = self.make_graph(history_shortcut)
556
 
        self.assertEqual({b'rev2b'}, graph.find_lca(b'rev3a', b'rev3b'))
 
555
        self.assertEqual({'rev2b'}, graph.find_lca('rev3a', 'rev3b'))
557
556
 
558
557
    def test_lefthand_distance_smoke(self):
559
558
        """A simple does it work test for graph.lefthand_distance(keys)."""
560
559
        graph = self.make_graph(history_shortcut)
561
 
        distance_graph = graph.find_lefthand_distances([b'rev3b', b'rev2a'])
562
 
        self.assertEqual({b'rev2a': 2, b'rev3b': 3}, distance_graph)
 
560
        distance_graph = graph.find_lefthand_distances(['rev3b', 'rev2a'])
 
561
        self.assertEqual({'rev2a': 2, 'rev3b': 3}, distance_graph)
563
562
 
564
563
    def test_lefthand_distance_ghosts(self):
565
564
        """A simple does it work test for graph.lefthand_distance(keys)."""
566
 
        nodes = {b'nonghost': [NULL_REVISION], b'toghost': [b'ghost']}
 
565
        nodes = {'nonghost':[NULL_REVISION], 'toghost':['ghost']}
567
566
        graph = self.make_graph(nodes)
568
 
        distance_graph = graph.find_lefthand_distances(
569
 
            [b'nonghost', b'toghost'])
570
 
        self.assertEqual({b'nonghost': 1, b'toghost': -1}, distance_graph)
 
567
        distance_graph = graph.find_lefthand_distances(['nonghost', 'toghost'])
 
568
        self.assertEqual({'nonghost': 1, 'toghost': -1}, distance_graph)
571
569
 
572
570
    def test_recursive_unique_lca(self):
573
571
        """Test finding a unique least common ancestor.
578
576
        self.assertEqual(NULL_REVISION,
579
577
                         graph.find_unique_lca(NULL_REVISION, NULL_REVISION))
580
578
        self.assertEqual(NULL_REVISION,
581
 
                         graph.find_unique_lca(NULL_REVISION, b'rev1'))
582
 
        self.assertEqual(b'rev1', graph.find_unique_lca(b'rev1', b'rev1'))
583
 
        self.assertEqual(b'rev1', graph.find_unique_lca(b'rev2a', b'rev2b'))
584
 
        self.assertEqual((b'rev1', 1,),
585
 
                         graph.find_unique_lca(b'rev2a', b'rev2b',
586
 
                                               count_steps=True))
 
579
                         graph.find_unique_lca(NULL_REVISION, 'rev1'))
 
580
        self.assertEqual('rev1', graph.find_unique_lca('rev1', 'rev1'))
 
581
        self.assertEqual('rev1', graph.find_unique_lca('rev2a', 'rev2b'))
 
582
        self.assertEqual(('rev1', 1,),
 
583
                         graph.find_unique_lca('rev2a', 'rev2b',
 
584
                         count_steps=True))
587
585
 
588
586
    def assertRemoveDescendants(self, expected, graph, revisions):
589
587
        parents = graph.get_parent_map(revisions)
592
590
 
593
591
    def test__remove_simple_descendants(self):
594
592
        graph = self.make_graph(ancestry_1)
595
 
        self.assertRemoveDescendants({b'rev1'}, graph,
596
 
                                     {b'rev1', b'rev2a', b'rev2b', b'rev3', b'rev4'})
 
593
        self.assertRemoveDescendants({'rev1'}, graph,
 
594
            {'rev1', 'rev2a', 'rev2b', 'rev3', 'rev4'})
597
595
 
598
596
    def test__remove_simple_descendants_disjoint(self):
599
597
        graph = self.make_graph(ancestry_1)
600
 
        self.assertRemoveDescendants({b'rev1', b'rev3'}, graph,
601
 
                                     {b'rev1', b'rev3'})
 
598
        self.assertRemoveDescendants({'rev1', 'rev3'}, graph,
 
599
            {'rev1', 'rev3'})
602
600
 
603
601
    def test__remove_simple_descendants_chain(self):
604
602
        graph = self.make_graph(ancestry_1)
605
 
        self.assertRemoveDescendants({b'rev1'}, graph,
606
 
                                     {b'rev1', b'rev2a', b'rev3'})
 
603
        self.assertRemoveDescendants({'rev1'}, graph,
 
604
            {'rev1', 'rev2a', 'rev3'})
607
605
 
608
606
    def test__remove_simple_descendants_siblings(self):
609
607
        graph = self.make_graph(ancestry_1)
610
 
        self.assertRemoveDescendants({b'rev2a', b'rev2b'}, graph,
611
 
                                     {b'rev2a', b'rev2b', b'rev3'})
 
608
        self.assertRemoveDescendants({'rev2a', 'rev2b'}, graph,
 
609
            {'rev2a', 'rev2b', 'rev3'})
612
610
 
613
611
    def test_unique_lca_criss_cross(self):
614
612
        """Ensure we don't pick non-unique lcas in a criss-cross"""
615
613
        graph = self.make_graph(criss_cross)
616
 
        self.assertEqual(b'rev1', graph.find_unique_lca(b'rev3a', b'rev3b'))
617
 
        lca, steps = graph.find_unique_lca(
618
 
            b'rev3a', b'rev3b', count_steps=True)
619
 
        self.assertEqual(b'rev1', lca)
 
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)
620
617
        self.assertEqual(2, steps)
621
618
 
622
619
    def test_unique_lca_null_revision(self):
623
620
        """Ensure we pick NULL_REVISION when necessary"""
624
621
        graph = self.make_graph(criss_cross2)
625
 
        self.assertEqual(b'rev1b', graph.find_unique_lca(b'rev2a', b'rev1b'))
 
622
        self.assertEqual('rev1b', graph.find_unique_lca('rev2a', 'rev1b'))
626
623
        self.assertEqual(NULL_REVISION,
627
 
                         graph.find_unique_lca(b'rev2a', b'rev2b'))
 
624
                         graph.find_unique_lca('rev2a', 'rev2b'))
628
625
 
629
626
    def test_unique_lca_null_revision2(self):
630
627
        """Ensure we pick NULL_REVISION when necessary"""
631
628
        graph = self.make_graph(ancestry_2)
632
629
        self.assertEqual(NULL_REVISION,
633
 
                         graph.find_unique_lca(b'rev4a', b'rev1b'))
 
630
                         graph.find_unique_lca('rev4a', 'rev1b'))
634
631
 
635
632
    def test_lca_double_shortcut(self):
636
633
        graph = self.make_graph(double_shortcut)
637
 
        self.assertEqual(b'c', graph.find_unique_lca(b'f', b'g'))
 
634
        self.assertEqual('c', graph.find_unique_lca('f', 'g'))
638
635
 
639
636
    def test_common_ancestor_two_repos(self):
640
637
        """Ensure we do unique_lca using data from two repos"""
650
647
 
651
648
        graph = mainline_tree.branch.repository.get_graph(
652
649
            feature_tree.branch.repository)
653
 
        self.assertEqual(b'rev2b', graph.find_unique_lca(b'rev2a', b'rev3b'))
 
650
        self.assertEqual('rev2b', graph.find_unique_lca('rev2a', 'rev3b'))
654
651
 
655
652
    def test_graph_difference(self):
656
653
        graph = self.make_graph(ancestry_1)
657
 
        self.assertEqual(
658
 
            (set(), set()), graph.find_difference(b'rev1', b'rev1'))
659
 
        self.assertEqual((set(), {b'rev1'}),
660
 
                         graph.find_difference(NULL_REVISION, b'rev1'))
661
 
        self.assertEqual(({b'rev1'}, set()),
662
 
                         graph.find_difference(b'rev1', NULL_REVISION))
663
 
        self.assertEqual(({b'rev2a', b'rev3'}, {b'rev2b'}),
664
 
                         graph.find_difference(b'rev3', b'rev2b'))
665
 
        self.assertEqual(({b'rev4', b'rev3', b'rev2a'}, set()),
666
 
                         graph.find_difference(b'rev4', b'rev2b'))
 
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'))
667
663
 
668
664
    def test_graph_difference_separate_ancestry(self):
669
665
        graph = self.make_graph(ancestry_2)
670
 
        self.assertEqual(({b'rev1a'}, {b'rev1b'}),
671
 
                         graph.find_difference(b'rev1a', b'rev1b'))
672
 
        self.assertEqual(({b'rev1a', b'rev2a', b'rev3a', b'rev4a'},
673
 
                          {b'rev1b'}),
674
 
                         graph.find_difference(b'rev4a', b'rev1b'))
 
666
        self.assertEqual(({'rev1a'}, {'rev1b'}),
 
667
                         graph.find_difference('rev1a', 'rev1b'))
 
668
        self.assertEqual(({'rev1a', 'rev2a', 'rev3a', 'rev4a'},
 
669
                          {'rev1b'}),
 
670
                         graph.find_difference('rev4a', 'rev1b'))
675
671
 
676
672
    def test_graph_difference_criss_cross(self):
677
673
        graph = self.make_graph(criss_cross)
678
 
        self.assertEqual(({b'rev3a'}, {b'rev3b'}),
679
 
                         graph.find_difference(b'rev3a', b'rev3b'))
680
 
        self.assertEqual((set([]), {b'rev3b', b'rev2b'}),
681
 
                         graph.find_difference(b'rev2a', b'rev3b'))
 
674
        self.assertEqual(({'rev3a'}, {'rev3b'}),
 
675
                         graph.find_difference('rev3a', 'rev3b'))
 
676
        self.assertEqual((set([]), {'rev3b', 'rev2b'}),
 
677
                         graph.find_difference('rev2a', 'rev3b'))
682
678
 
683
679
    def test_graph_difference_extended_history(self):
684
680
        graph = self.make_graph(extended_history_shortcut)
685
 
        self.assertEqual(({b'e'}, {b'f'}),
686
 
                         graph.find_difference(b'e', b'f'))
687
 
        self.assertEqual(({b'f'}, {b'e'}),
688
 
                         graph.find_difference(b'f', b'e'))
 
681
        self.assertEqual(({'e'}, {'f'}),
 
682
                         graph.find_difference('e', 'f'))
 
683
        self.assertEqual(({'f'}, {'e'}),
 
684
                         graph.find_difference('f', 'e'))
689
685
 
690
686
    def test_graph_difference_double_shortcut(self):
691
687
        graph = self.make_graph(double_shortcut)
692
 
        self.assertEqual(({b'd', b'f'}, {b'e', b'g'}),
693
 
                         graph.find_difference(b'f', b'g'))
 
688
        self.assertEqual(({'d', 'f'}, {'e', 'g'}),
 
689
                         graph.find_difference('f', 'g'))
694
690
 
695
691
    def test_graph_difference_complex_shortcut(self):
696
692
        graph = self.make_graph(complex_shortcut)
697
 
        self.assertEqual(({b'm', b'i', b'e'}, {b'n', b'h'}),
698
 
                         graph.find_difference(b'm', b'n'))
 
693
        self.assertEqual(({'m', 'i', 'e'}, {'n', 'h'}),
 
694
                         graph.find_difference('m', 'n'))
699
695
 
700
696
    def test_graph_difference_complex_shortcut2(self):
701
697
        graph = self.make_graph(complex_shortcut2)
702
 
        self.assertEqual(({b't'}, {b'j', b'u'}),
703
 
                         graph.find_difference(b't', b'u'))
 
698
        self.assertEqual(({'t'}, {'j', 'u'}),
 
699
                         graph.find_difference('t', 'u'))
704
700
 
705
701
    def test_graph_difference_shortcut_extra_root(self):
706
702
        graph = self.make_graph(shortcut_extra_root)
707
 
        self.assertEqual(({b'e'}, {b'f', b'g'}),
708
 
                         graph.find_difference(b'e', b'f'))
 
703
        self.assertEqual(({'e'}, {'f', 'g'}),
 
704
                         graph.find_difference('e', 'f'))
709
705
 
710
706
    def test_iter_topo_order(self):
711
707
        graph = self.make_graph(ancestry_1)
712
 
        args = [b'rev2a', b'rev3', b'rev1']
 
708
        args = ['rev2a', 'rev3', 'rev1']
713
709
        topo_args = list(graph.iter_topo_order(args))
714
710
        self.assertEqual(set(args), set(topo_args))
715
 
        self.assertTrue(topo_args.index(b'rev2a') > topo_args.index(b'rev1'))
716
 
        self.assertTrue(topo_args.index(b'rev2a') < topo_args.index(b'rev3'))
 
711
        self.assertTrue(topo_args.index('rev2a') > topo_args.index('rev1'))
 
712
        self.assertTrue(topo_args.index('rev2a') < topo_args.index('rev3'))
717
713
 
718
714
    def test_is_ancestor(self):
719
715
        graph = self.make_graph(ancestry_1)
720
 
        self.assertEqual(True, graph.is_ancestor(b'null:', b'null:'))
721
 
        self.assertEqual(True, graph.is_ancestor(b'null:', b'rev1'))
722
 
        self.assertEqual(False, graph.is_ancestor(b'rev1', b'null:'))
723
 
        self.assertEqual(True, graph.is_ancestor(b'null:', b'rev4'))
724
 
        self.assertEqual(False, graph.is_ancestor(b'rev4', b'null:'))
725
 
        self.assertEqual(False, graph.is_ancestor(b'rev4', b'rev2b'))
726
 
        self.assertEqual(True, graph.is_ancestor(b'rev2b', b'rev4'))
727
 
        self.assertEqual(False, graph.is_ancestor(b'rev2b', b'rev3'))
728
 
        self.assertEqual(False, graph.is_ancestor(b'rev3', b'rev2b'))
 
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'))
729
725
        instrumented_provider = InstrumentedParentsProvider(graph)
730
726
        instrumented_graph = _mod_graph.Graph(instrumented_provider)
731
 
        instrumented_graph.is_ancestor(b'rev2a', b'rev2b')
732
 
        self.assertTrue(b'null:' not in instrumented_provider.calls)
 
727
        instrumented_graph.is_ancestor('rev2a', 'rev2b')
 
728
        self.assertTrue('null:' not in instrumented_provider.calls)
733
729
 
734
730
    def test_is_between(self):
735
731
        graph = self.make_graph(ancestry_1)
736
 
        self.assertEqual(True, graph.is_between(b'null:', b'null:', b'null:'))
737
 
        self.assertEqual(True, graph.is_between(b'rev1', b'null:', b'rev1'))
738
 
        self.assertEqual(True, graph.is_between(b'rev1', b'rev1', b'rev4'))
739
 
        self.assertEqual(True, graph.is_between(b'rev4', b'rev1', b'rev4'))
740
 
        self.assertEqual(True, graph.is_between(b'rev3', b'rev1', b'rev4'))
741
 
        self.assertEqual(False, graph.is_between(b'rev4', b'rev1', b'rev3'))
742
 
        self.assertEqual(False, graph.is_between(b'rev1', b'rev2a', b'rev4'))
743
 
        self.assertEqual(False, graph.is_between(b'null:', b'rev1', b'rev4'))
 
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'))
744
740
 
745
741
    def test_is_ancestor_boundary(self):
746
742
        """Ensure that we avoid searching the whole graph.
751
747
        graph = self.make_graph(boundary)
752
748
        instrumented_provider = InstrumentedParentsProvider(graph)
753
749
        graph = _mod_graph.Graph(instrumented_provider)
754
 
        self.assertFalse(graph.is_ancestor(b'a', b'c'))
755
 
        self.assertTrue(b'null:' not in instrumented_provider.calls)
 
750
        self.assertFalse(graph.is_ancestor('a', 'c'))
 
751
        self.assertTrue('null:' not in instrumented_provider.calls)
756
752
 
757
753
    def test_iter_ancestry(self):
758
754
        nodes = boundary.copy()
759
755
        nodes[NULL_REVISION] = ()
760
756
        graph = self.make_graph(nodes)
761
757
        expected = nodes.copy()
762
 
        expected.pop(b'a')  # b'a' is not in the ancestry of b'c', all the
763
 
        # other nodes are
764
 
        self.assertEqual(expected, dict(graph.iter_ancestry([b'c'])))
765
 
        self.assertEqual(nodes, dict(graph.iter_ancestry([b'a', b'c'])))
 
758
        expected.pop('a') # 'a' is not in the ancestry of 'c', all the
 
759
                          # other nodes are
 
760
        self.assertEqual(expected, dict(graph.iter_ancestry(['c'])))
 
761
        self.assertEqual(nodes, dict(graph.iter_ancestry(['a', 'c'])))
766
762
 
767
763
    def test_iter_ancestry_with_ghost(self):
768
764
        graph = self.make_graph(with_ghost)
769
765
        expected = with_ghost.copy()
770
 
        # b'a' is not in the ancestry of b'c', and b'g' is a ghost
771
 
        expected[b'g'] = None
772
 
        self.assertEqual(expected, dict(graph.iter_ancestry([b'a', b'c'])))
773
 
        expected.pop(b'a')
774
 
        self.assertEqual(expected, dict(graph.iter_ancestry([b'c'])))
 
766
        # 'a' is not in the ancestry of 'c', and 'g' is a ghost
 
767
        expected['g'] = None
 
768
        self.assertEqual(expected, dict(graph.iter_ancestry(['a', 'c'])))
 
769
        expected.pop('a')
 
770
        self.assertEqual(expected, dict(graph.iter_ancestry(['c'])))
775
771
 
776
772
    def test_filter_candidate_lca(self):
777
773
        """Test filter_candidate_lca for a corner case
778
774
 
779
 
        This tests the case where we encounter the end of iteration for b'e'
780
 
        in the same pass as we discover that b'd' is an ancestor of b'e', and
781
 
        therefore b'e' can't be an lca.
 
775
        This tests the case where we encounter the end of iteration for 'e'
 
776
        in the same pass as we discover that 'd' is an ancestor of 'e', and
 
777
        therefore 'e' can't be an lca.
782
778
 
783
779
        To compensate for different dict orderings on other Python
784
 
        implementations, we mirror b'd' and b'e' with b'b' and b'a'.
 
780
        implementations, we mirror 'd' and 'e' with 'b' and 'a'.
785
781
        """
786
782
        # This test is sensitive to the iteration order of dicts.  It will
787
 
        # pass incorrectly if b'e' and b'a' sort before b'c'
 
783
        # pass incorrectly if 'e' and 'a' sort before 'c'
788
784
        #
789
785
        # NULL_REVISION
790
786
        #     / \
793
789
        #    b   d
794
790
        #     \ /
795
791
        #      c
796
 
        graph = self.make_graph({b'c': [b'b', b'd'], b'd': [b'e'], b'b': [b'a'],
797
 
                                 b'a': [NULL_REVISION], b'e': [NULL_REVISION]})
798
 
        self.assertEqual({b'c'}, graph.heads([b'a', b'c', b'e']))
 
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']))
799
795
 
800
796
    def test_heads_null(self):
801
797
        graph = self.make_graph(ancestry_1)
802
 
        self.assertEqual({b'null:'}, graph.heads([b'null:']))
803
 
        self.assertEqual({b'rev1'}, graph.heads([b'null:', b'rev1']))
804
 
        self.assertEqual({b'rev1'}, graph.heads([b'rev1', b'null:']))
805
 
        self.assertEqual({b'rev1'}, graph.heads({b'rev1', b'null:'}))
806
 
        self.assertEqual({b'rev1'}, graph.heads((b'rev1', b'null:')))
 
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:')))
807
803
 
808
804
    def test_heads_one(self):
809
805
        # A single node will always be a head
810
806
        graph = self.make_graph(ancestry_1)
811
 
        self.assertEqual({b'null:'}, graph.heads([b'null:']))
812
 
        self.assertEqual({b'rev1'}, graph.heads([b'rev1']))
813
 
        self.assertEqual({b'rev2a'}, graph.heads([b'rev2a']))
814
 
        self.assertEqual({b'rev2b'}, graph.heads([b'rev2b']))
815
 
        self.assertEqual({b'rev3'}, graph.heads([b'rev3']))
816
 
        self.assertEqual({b'rev4'}, graph.heads([b'rev4']))
 
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']))
817
813
 
818
814
    def test_heads_single(self):
819
815
        graph = self.make_graph(ancestry_1)
820
 
        self.assertEqual({b'rev4'}, graph.heads([b'null:', b'rev4']))
821
 
        self.assertEqual({b'rev2a'}, graph.heads([b'rev1', b'rev2a']))
822
 
        self.assertEqual({b'rev2b'}, graph.heads([b'rev1', b'rev2b']))
823
 
        self.assertEqual({b'rev3'}, graph.heads([b'rev1', b'rev3']))
824
 
        self.assertEqual({b'rev4'}, graph.heads([b'rev1', b'rev4']))
825
 
        self.assertEqual({b'rev4'}, graph.heads([b'rev2a', b'rev4']))
826
 
        self.assertEqual({b'rev4'}, graph.heads([b'rev2b', b'rev4']))
827
 
        self.assertEqual({b'rev4'}, graph.heads([b'rev3', b'rev4']))
 
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']))
828
824
 
829
825
    def test_heads_two_heads(self):
830
826
        graph = self.make_graph(ancestry_1)
831
 
        self.assertEqual({b'rev2a', b'rev2b'},
832
 
                         graph.heads([b'rev2a', b'rev2b']))
833
 
        self.assertEqual({b'rev3', b'rev2b'},
834
 
                         graph.heads([b'rev3', b'rev2b']))
 
827
        self.assertEqual({'rev2a', 'rev2b'},
 
828
                         graph.heads(['rev2a', 'rev2b']))
 
829
        self.assertEqual({'rev3', 'rev2b'},
 
830
                         graph.heads(['rev3', 'rev2b']))
835
831
 
836
832
    def test_heads_criss_cross(self):
837
833
        graph = self.make_graph(criss_cross)
838
 
        self.assertEqual({b'rev2a'},
839
 
                         graph.heads([b'rev2a', b'rev1']))
840
 
        self.assertEqual({b'rev2b'},
841
 
                         graph.heads([b'rev2b', b'rev1']))
842
 
        self.assertEqual({b'rev3a'},
843
 
                         graph.heads([b'rev3a', b'rev1']))
844
 
        self.assertEqual({b'rev3b'},
845
 
                         graph.heads([b'rev3b', b'rev1']))
846
 
        self.assertEqual({b'rev2a', b'rev2b'},
847
 
                         graph.heads([b'rev2a', b'rev2b']))
848
 
        self.assertEqual({b'rev3a'},
849
 
                         graph.heads([b'rev3a', b'rev2a']))
850
 
        self.assertEqual({b'rev3a'},
851
 
                         graph.heads([b'rev3a', b'rev2b']))
852
 
        self.assertEqual({b'rev3a'},
853
 
                         graph.heads([b'rev3a', b'rev2a', b'rev2b']))
854
 
        self.assertEqual({b'rev3b'},
855
 
                         graph.heads([b'rev3b', b'rev2a']))
856
 
        self.assertEqual({b'rev3b'},
857
 
                         graph.heads([b'rev3b', b'rev2b']))
858
 
        self.assertEqual({b'rev3b'},
859
 
                         graph.heads([b'rev3b', b'rev2a', b'rev2b']))
860
 
        self.assertEqual({b'rev3a', b'rev3b'},
861
 
                         graph.heads([b'rev3a', b'rev3b']))
862
 
        self.assertEqual({b'rev3a', b'rev3b'},
863
 
                         graph.heads([b'rev3a', b'rev3b', b'rev2a', b'rev2b']))
 
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']))
864
860
 
865
861
    def test_heads_shortcut(self):
866
862
        graph = self.make_graph(history_shortcut)
867
863
 
868
 
        self.assertEqual({b'rev2a', b'rev2b', b'rev2c'},
869
 
                         graph.heads([b'rev2a', b'rev2b', b'rev2c']))
870
 
        self.assertEqual({b'rev3a', b'rev3b'},
871
 
                         graph.heads([b'rev3a', b'rev3b']))
872
 
        self.assertEqual({b'rev3a', b'rev3b'},
873
 
                         graph.heads([b'rev2a', b'rev3a', b'rev3b']))
874
 
        self.assertEqual({b'rev2a', b'rev3b'},
875
 
                         graph.heads([b'rev2a', b'rev3b']))
876
 
        self.assertEqual({b'rev2c', b'rev3a'},
877
 
                         graph.heads([b'rev2c', b'rev3a']))
 
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']))
878
874
 
879
875
    def _run_heads_break_deeper(self, graph_dict, search):
880
876
        """Run heads on a graph-as-a-dict.
881
877
 
882
 
        If the search asks for the parents of b'deeper' the test will fail.
 
878
        If the search asks for the parents of 'deeper' the test will fail.
883
879
        """
884
880
        class stub(object):
885
881
            pass
886
 
 
887
882
        def get_parent_map(keys):
888
883
            result = {}
889
884
            for key in keys:
890
 
                if key == b'deeper':
 
885
                if key == 'deeper':
891
886
                    self.fail('key deeper was accessed')
892
887
                result[key] = graph_dict[key]
893
888
            return result
899
894
    def test_heads_limits_search(self):
900
895
        # test that a heads query does not search all of history
901
896
        graph_dict = {
902
 
            b'left': [b'common'],
903
 
            b'right': [b'common'],
904
 
            b'common': [b'deeper'],
 
897
            'left':['common'],
 
898
            'right':['common'],
 
899
            'common':['deeper'],
905
900
        }
906
 
        self.assertEqual({b'left', b'right'},
907
 
                         self._run_heads_break_deeper(graph_dict, [b'left', b'right']))
 
901
        self.assertEqual({'left', 'right'},
 
902
            self._run_heads_break_deeper(graph_dict, ['left', 'right']))
908
903
 
909
904
    def test_heads_limits_search_assymetric(self):
910
905
        # test that a heads query does not search all of history
911
906
        graph_dict = {
912
 
            b'left': [b'midleft'],
913
 
            b'midleft': [b'common'],
914
 
            b'right': [b'common'],
915
 
            b'common': [b'aftercommon'],
916
 
            b'aftercommon': [b'deeper'],
 
907
            'left':['midleft'],
 
908
            'midleft':['common'],
 
909
            'right':['common'],
 
910
            'common':['aftercommon'],
 
911
            'aftercommon':['deeper'],
917
912
        }
918
 
        self.assertEqual({b'left', b'right'},
919
 
                         self._run_heads_break_deeper(graph_dict, [b'left', b'right']))
 
913
        self.assertEqual({'left', 'right'},
 
914
            self._run_heads_break_deeper(graph_dict, ['left', 'right']))
920
915
 
921
916
    def test_heads_limits_search_common_search_must_continue(self):
922
917
        # test that common nodes are still queried, preventing
923
918
        # all-the-way-to-origin behaviour in the following graph:
924
919
        graph_dict = {
925
 
            b'h1': [b'shortcut', b'common1'],
926
 
            b'h2': [b'common1'],
927
 
            b'shortcut': [b'common2'],
928
 
            b'common1': [b'common2'],
929
 
            b'common2': [b'deeper'],
 
920
            'h1':['shortcut', 'common1'],
 
921
            'h2':['common1'],
 
922
            'shortcut':['common2'],
 
923
            'common1':['common2'],
 
924
            'common2':['deeper'],
930
925
        }
931
 
        self.assertEqual({b'h1', b'h2'},
932
 
                         self._run_heads_break_deeper(graph_dict, [b'h1', b'h2']))
 
926
        self.assertEqual({'h1', 'h2'},
 
927
            self._run_heads_break_deeper(graph_dict, ['h1', 'h2']))
933
928
 
934
929
    def test_breadth_first_search_start_ghosts(self):
935
930
        graph = self.make_graph({})
936
931
        # with_ghosts reports the ghosts
937
 
        search = graph._make_breadth_first_searcher([b'a-ghost'])
938
 
        self.assertEqual((set(), {b'a-ghost'}), search.next_with_ghosts())
 
932
        search = graph._make_breadth_first_searcher(['a-ghost'])
 
933
        self.assertEqual((set(), {'a-ghost'}), search.next_with_ghosts())
939
934
        self.assertRaises(StopIteration, search.next_with_ghosts)
940
935
        # next includes them
941
 
        search = graph._make_breadth_first_searcher([b'a-ghost'])
942
 
        self.assertEqual({b'a-ghost'}, next(search))
 
936
        search = graph._make_breadth_first_searcher(['a-ghost'])
 
937
        self.assertEqual({'a-ghost'}, next(search))
943
938
        self.assertRaises(StopIteration, next, search)
944
939
 
945
940
    def test_breadth_first_search_deep_ghosts(self):
946
941
        graph = self.make_graph({
947
 
            b'head': [b'present'],
948
 
            b'present': [b'child', b'ghost'],
949
 
            b'child': [],
 
942
            'head':['present'],
 
943
            'present':['child', 'ghost'],
 
944
            'child':[],
950
945
            })
951
946
        # with_ghosts reports the ghosts
952
 
        search = graph._make_breadth_first_searcher([b'head'])
953
 
        self.assertEqual(({b'head'}, set()), search.next_with_ghosts())
954
 
        self.assertEqual(({b'present'}, set()), search.next_with_ghosts())
955
 
        self.assertEqual(({b'child'}, {b'ghost'}),
956
 
                         search.next_with_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'}),
 
951
            search.next_with_ghosts())
957
952
        self.assertRaises(StopIteration, search.next_with_ghosts)
958
953
        # next includes them
959
 
        search = graph._make_breadth_first_searcher([b'head'])
960
 
        self.assertEqual({b'head'}, next(search))
961
 
        self.assertEqual({b'present'}, next(search))
962
 
        self.assertEqual({b'child', b'ghost'},
963
 
                         next(search))
 
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'},
 
958
            next(search))
964
959
        self.assertRaises(StopIteration, next, search)
965
960
 
966
961
    def test_breadth_first_search_change_next_to_next_with_ghosts(self):
967
962
        # To make the API robust, we allow calling both next() and
968
963
        # next_with_ghosts() on the same searcher.
969
964
        graph = self.make_graph({
970
 
            b'head': [b'present'],
971
 
            b'present': [b'child', b'ghost'],
972
 
            b'child': [],
 
965
            'head':['present'],
 
966
            'present':['child', 'ghost'],
 
967
            'child':[],
973
968
            })
974
969
        # start with next_with_ghosts
975
 
        search = graph._make_breadth_first_searcher([b'head'])
976
 
        self.assertEqual(({b'head'}, set()), search.next_with_ghosts())
977
 
        self.assertEqual({b'present'}, next(search))
978
 
        self.assertEqual(({b'child'}, {b'ghost'}),
979
 
                         search.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'}),
 
974
            search.next_with_ghosts())
980
975
        self.assertRaises(StopIteration, next, search)
981
976
        # start with next
982
 
        search = graph._make_breadth_first_searcher([b'head'])
983
 
        self.assertEqual({b'head'}, next(search))
984
 
        self.assertEqual(({b'present'}, set()), search.next_with_ghosts())
985
 
        self.assertEqual({b'child', b'ghost'},
986
 
                         next(search))
 
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'},
 
981
            next(search))
987
982
        self.assertRaises(StopIteration, search.next_with_ghosts)
988
983
 
989
984
    def test_breadth_first_change_search(self):
990
985
        # Changing the search should work with both next and next_with_ghosts.
991
986
        graph = self.make_graph({
992
 
            b'head': [b'present'],
993
 
            b'present': [b'stopped'],
994
 
            b'other': [b'other_2'],
995
 
            b'other_2': [],
 
987
            'head':['present'],
 
988
            'present':['stopped'],
 
989
            'other':['other_2'],
 
990
            'other_2':[],
996
991
            })
997
 
        search = graph._make_breadth_first_searcher([b'head'])
998
 
        self.assertEqual(({b'head'}, set()), search.next_with_ghosts())
999
 
        self.assertEqual(({b'present'}, set()), search.next_with_ghosts())
1000
 
        self.assertEqual({b'present'},
1001
 
                         search.stop_searching_any([b'present']))
1002
 
        self.assertEqual(({b'other'}, {b'other_ghost'}),
1003
 
                         search.start_searching([b'other', b'other_ghost']))
1004
 
        self.assertEqual(({b'other_2'}, set()), search.next_with_ghosts())
 
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())
1005
1000
        self.assertRaises(StopIteration, search.next_with_ghosts)
1006
1001
        # next includes them
1007
 
        search = graph._make_breadth_first_searcher([b'head'])
1008
 
        self.assertEqual({b'head'}, next(search))
1009
 
        self.assertEqual({b'present'}, next(search))
1010
 
        self.assertEqual({b'present'},
1011
 
                         search.stop_searching_any([b'present']))
1012
 
        search.start_searching([b'other', b'other_ghost'])
1013
 
        self.assertEqual({b'other_2'}, next(search))
 
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))
1014
1009
        self.assertRaises(StopIteration, next, search)
1015
1010
 
1016
1011
    def assertSeenAndResult(self, instructions, search, next):
1040
1035
 
1041
1036
    def test_breadth_first_get_result_excludes_current_pending(self):
1042
1037
        graph = self.make_graph({
1043
 
            b'head': [b'child'],
1044
 
            b'child': [NULL_REVISION],
1045
 
            NULL_REVISION: [],
 
1038
            'head':['child'],
 
1039
            'child':[NULL_REVISION],
 
1040
            NULL_REVISION:[],
1046
1041
            })
1047
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1042
        search = graph._make_breadth_first_searcher(['head'])
1048
1043
        # At the start, nothing has been seen, to its all excluded:
1049
1044
        state = search.get_state()
1050
 
        self.assertEqual(({b'head'}, {b'head'}, set()),
1051
 
                         state)
 
1045
        self.assertEqual(({'head'}, {'head'}, set()),
 
1046
            state)
1052
1047
        self.assertEqual(set(), search.seen)
1053
1048
        # using next:
1054
1049
        expected = [
1055
 
            ({b'head'}, ({b'head'}, {b'child'}, 1),
1056
 
             [b'head'], None, None),
1057
 
            ({b'head', b'child'}, ({b'head'}, {NULL_REVISION}, 2),
1058
 
             [b'head', b'child'], None, None),
1059
 
            ({b'head', b'child', NULL_REVISION}, ({b'head'}, set(), 3),
1060
 
             [b'head', b'child', NULL_REVISION], None, None),
 
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),
1061
1056
            ]
1062
1057
        self.assertSeenAndResult(expected, search, search.__next__)
1063
1058
        # using next_with_ghosts:
1064
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1059
        search = graph._make_breadth_first_searcher(['head'])
1065
1060
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1066
1061
 
1067
1062
    def test_breadth_first_get_result_starts_stops(self):
1068
1063
        graph = self.make_graph({
1069
 
            b'head': [b'child'],
1070
 
            b'child': [NULL_REVISION],
1071
 
            b'otherhead': [b'otherchild'],
1072
 
            b'otherchild': [b'excluded'],
1073
 
            b'excluded': [NULL_REVISION],
1074
 
            NULL_REVISION: []
 
1064
            'head':['child'],
 
1065
            'child':[NULL_REVISION],
 
1066
            'otherhead':['otherchild'],
 
1067
            'otherchild':['excluded'],
 
1068
            'excluded':[NULL_REVISION],
 
1069
            NULL_REVISION:[]
1075
1070
            })
1076
1071
        search = graph._make_breadth_first_searcher([])
1077
1072
        # Starting with nothing and adding a search works:
1078
 
        search.start_searching([b'head'])
 
1073
        search.start_searching(['head'])
1079
1074
        # head has been seen:
1080
1075
        state = search.get_state()
1081
 
        self.assertEqual(({b'head'}, {b'child'}, {b'head'}),
1082
 
                         state)
1083
 
        self.assertEqual({b'head'}, search.seen)
 
1076
        self.assertEqual(({'head'}, {'child'}, {'head'}),
 
1077
            state)
 
1078
        self.assertEqual({'head'}, search.seen)
1084
1079
        # using next:
1085
1080
        expected = [
1086
1081
            # stop at child, and start a new search at otherhead:
1087
1082
            # - otherhead counts as seen immediately when start_searching is
1088
1083
            # called.
1089
 
            ({b'head', b'child', b'otherhead'},
1090
 
             ({b'head', b'otherhead'}, {b'child', b'otherchild'}, 2),
1091
 
             [b'head', b'otherhead'], [b'otherhead'], [b'child']),
1092
 
            ({b'head', b'child', b'otherhead', b'otherchild'},
1093
 
             ({b'head', b'otherhead'}, {b'child', b'excluded'}, 3),
1094
 
             [b'head', b'otherhead', b'otherchild'], None, None),
 
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),
1095
1090
            # stop searching excluded now
1096
 
            ({b'head', b'child', b'otherhead', b'otherchild', b'excluded'},
1097
 
             ({b'head', b'otherhead'}, {b'child', b'excluded'}, 3),
1098
 
             [b'head', b'otherhead', b'otherchild'], None, [b'excluded']),
 
1091
            ({'head', 'child', 'otherhead', 'otherchild', 'excluded'},
 
1092
             ({'head', 'otherhead'}, {'child', 'excluded'}, 3),
 
1093
             ['head', 'otherhead', 'otherchild'], None, ['excluded']),
1099
1094
            ]
1100
1095
        self.assertSeenAndResult(expected, search, search.__next__)
1101
1096
        # using next_with_ghosts:
1102
1097
        search = graph._make_breadth_first_searcher([])
1103
 
        search.start_searching([b'head'])
 
1098
        search.start_searching(['head'])
1104
1099
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1105
1100
 
1106
1101
    def test_breadth_first_stop_searching_not_queried(self):
1107
 
        # A client should be able to say b'stop node X' even if X has not been
 
1102
        # A client should be able to say 'stop node X' even if X has not been
1108
1103
        # returned to the client.
1109
1104
        graph = self.make_graph({
1110
 
            b'head': [b'child', b'ghost1'],
1111
 
            b'child': [NULL_REVISION],
1112
 
            NULL_REVISION: [],
 
1105
            'head':['child', 'ghost1'],
 
1106
            'child':[NULL_REVISION],
 
1107
            NULL_REVISION:[],
1113
1108
            })
1114
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1109
        search = graph._make_breadth_first_searcher(['head'])
1115
1110
        expected = [
1116
1111
            # NULL_REVISION and ghost1 have not been returned
1117
 
            ({b'head'},
1118
 
             ({b'head'}, {b'child', NULL_REVISION, b'ghost1'}, 1),
1119
 
             [b'head'], None, [NULL_REVISION, b'ghost1']),
 
1112
            ({'head'},
 
1113
             ({'head'}, {'child', NULL_REVISION, 'ghost1'}, 1),
 
1114
             ['head'], None, [NULL_REVISION, 'ghost1']),
1120
1115
            # ghost1 has been returned, NULL_REVISION is to be returned in the
1121
1116
            # next iteration.
1122
 
            ({b'head', b'child', b'ghost1'},
1123
 
             ({b'head'}, {b'ghost1', NULL_REVISION}, 2),
1124
 
             [b'head', b'child'], None, [NULL_REVISION, b'ghost1']),
 
1117
            ({'head', 'child', 'ghost1'},
 
1118
             ({'head'}, {'ghost1', NULL_REVISION}, 2),
 
1119
             ['head', 'child'], None, [NULL_REVISION, 'ghost1']),
1125
1120
            ]
1126
1121
        self.assertSeenAndResult(expected, search, search.__next__)
1127
1122
        # using next_with_ghosts:
1128
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1123
        search = graph._make_breadth_first_searcher(['head'])
1129
1124
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1130
1125
 
1131
1126
    def test_breadth_first_stop_searching_late(self):
1132
 
        # A client should be able to say b'stop node X' and have it excluded
 
1127
        # A client should be able to say 'stop node X' and have it excluded
1133
1128
        # from the result even if X was seen in an older iteration of the
1134
1129
        # search.
1135
1130
        graph = self.make_graph({
1136
 
            b'head': [b'middle'],
1137
 
            b'middle': [b'child'],
1138
 
            b'child': [NULL_REVISION],
1139
 
            NULL_REVISION: [],
 
1131
            'head':['middle'],
 
1132
            'middle':['child'],
 
1133
            'child':[NULL_REVISION],
 
1134
            NULL_REVISION:[],
1140
1135
            })
1141
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1136
        search = graph._make_breadth_first_searcher(['head'])
1142
1137
        expected = [
1143
 
            ({b'head'}, ({b'head'}, {b'middle'}, 1),
1144
 
             [b'head'], None, None),
1145
 
            ({b'head', b'middle'}, ({b'head'}, {b'child'}, 2),
1146
 
             [b'head', b'middle'], None, None),
1147
 
            # b'middle' came from the previous iteration, but we don't stop
 
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
1148
1143
            # searching it until *after* advancing the searcher.
1149
 
            ({b'head', b'middle', b'child'},
1150
 
             ({b'head'}, {b'middle', b'child'}, 1),
1151
 
             [b'head'], None, [b'middle', b'child']),
 
1144
            ({'head', 'middle', 'child'},
 
1145
             ({'head'}, {'middle', 'child'}, 1),
 
1146
             ['head'], None, ['middle', 'child']),
1152
1147
            ]
1153
1148
        self.assertSeenAndResult(expected, search, search.__next__)
1154
1149
        # using next_with_ghosts:
1155
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1150
        search = graph._make_breadth_first_searcher(['head'])
1156
1151
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1157
1152
 
1158
1153
    def test_breadth_first_get_result_ghosts_are_excluded(self):
1159
1154
        graph = self.make_graph({
1160
 
            b'head': [b'child', b'ghost'],
1161
 
            b'child': [NULL_REVISION],
1162
 
            NULL_REVISION: [],
 
1155
            'head':['child', 'ghost'],
 
1156
            'child':[NULL_REVISION],
 
1157
            NULL_REVISION:[],
1163
1158
            })
1164
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1159
        search = graph._make_breadth_first_searcher(['head'])
1165
1160
        # using next:
1166
1161
        expected = [
1167
 
            ({b'head'},
1168
 
             ({b'head'}, {b'ghost', b'child'}, 1),
1169
 
             [b'head'], None, None),
1170
 
            ({b'head', b'child', b'ghost'},
1171
 
             ({b'head'}, {NULL_REVISION, b'ghost'}, 2),
1172
 
             [b'head', b'child'], None, None),
 
1162
            ({'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),
1173
1168
            ]
1174
1169
        self.assertSeenAndResult(expected, search, search.__next__)
1175
1170
        # using next_with_ghosts:
1176
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1171
        search = graph._make_breadth_first_searcher(['head'])
1177
1172
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1178
1173
 
1179
1174
    def test_breadth_first_get_result_starting_a_ghost_ghost_is_excluded(self):
1180
1175
        graph = self.make_graph({
1181
 
            b'head': [b'child'],
1182
 
            b'child': [NULL_REVISION],
1183
 
            NULL_REVISION: [],
 
1176
            'head':['child'],
 
1177
            'child':[NULL_REVISION],
 
1178
            NULL_REVISION:[],
1184
1179
            })
1185
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1180
        search = graph._make_breadth_first_searcher(['head'])
1186
1181
        # using next:
1187
1182
        expected = [
1188
 
            ({b'head', b'ghost'},
1189
 
             ({b'head', b'ghost'}, {b'child', b'ghost'}, 1),
1190
 
             [b'head'], [b'ghost'], None),
1191
 
            ({b'head', b'child', b'ghost'},
1192
 
             ({b'head', b'ghost'}, {NULL_REVISION, b'ghost'}, 2),
1193
 
             [b'head', b'child'], None, None),
 
1183
            ({'head', 'ghost'},
 
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),
1194
1189
            ]
1195
1190
        self.assertSeenAndResult(expected, search, search.__next__)
1196
1191
        # using next_with_ghosts:
1197
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1192
        search = graph._make_breadth_first_searcher(['head'])
1198
1193
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1199
1194
 
1200
1195
    def test_breadth_first_revision_count_includes_NULL_REVISION(self):
1201
1196
        graph = self.make_graph({
1202
 
            b'head': [NULL_REVISION],
1203
 
            NULL_REVISION: [],
 
1197
            'head':[NULL_REVISION],
 
1198
            NULL_REVISION:[],
1204
1199
            })
1205
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1200
        search = graph._make_breadth_first_searcher(['head'])
1206
1201
        # using next:
1207
1202
        expected = [
1208
 
            ({b'head'},
1209
 
             ({b'head'}, {NULL_REVISION}, 1),
1210
 
             [b'head'], None, None),
1211
 
            ({b'head', NULL_REVISION},
1212
 
             ({b'head'}, set([]), 2),
1213
 
             [b'head', NULL_REVISION], None, None),
 
1203
            ({'head'},
 
1204
             ({'head'}, {NULL_REVISION}, 1),
 
1205
             ['head'], None, None),
 
1206
            ({'head', NULL_REVISION},
 
1207
             ({'head'}, set([]), 2),
 
1208
             ['head', NULL_REVISION], None, None),
1214
1209
            ]
1215
1210
        self.assertSeenAndResult(expected, search, search.__next__)
1216
1211
        # using next_with_ghosts:
1217
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1212
        search = graph._make_breadth_first_searcher(['head'])
1218
1213
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1219
1214
 
1220
1215
    def test_breadth_first_search_get_result_after_StopIteration(self):
1221
1216
        # StopIteration should not invalid anything..
1222
1217
        graph = self.make_graph({
1223
 
            b'head': [NULL_REVISION],
1224
 
            NULL_REVISION: [],
 
1218
            'head':[NULL_REVISION],
 
1219
            NULL_REVISION:[],
1225
1220
            })
1226
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1221
        search = graph._make_breadth_first_searcher(['head'])
1227
1222
        # using next:
1228
1223
        expected = [
1229
 
            ({b'head'},
1230
 
             ({b'head'}, {NULL_REVISION}, 1),
1231
 
             [b'head'], None, None),
1232
 
            ({b'head', b'ghost', NULL_REVISION},
1233
 
             ({b'head', b'ghost'}, {b'ghost'}, 2),
1234
 
             [b'head', NULL_REVISION], [b'ghost'], None),
 
1224
            ({'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),
1235
1230
            ]
1236
1231
        self.assertSeenAndResult(expected, search, search.__next__)
1237
1232
        self.assertRaises(StopIteration, next, search)
1238
 
        self.assertEqual({b'head', b'ghost', NULL_REVISION}, search.seen)
 
1233
        self.assertEqual({'head', 'ghost', NULL_REVISION}, search.seen)
1239
1234
        state = search.get_state()
1240
1235
        self.assertEqual(
1241
 
            ({b'ghost', b'head'}, {b'ghost'},
1242
 
                {b'head', NULL_REVISION}),
 
1236
            ({'ghost', 'head'}, {'ghost'},
 
1237
                {'head', NULL_REVISION}),
1243
1238
            state)
1244
1239
        # using next_with_ghosts:
1245
 
        search = graph._make_breadth_first_searcher([b'head'])
 
1240
        search = graph._make_breadth_first_searcher(['head'])
1246
1241
        self.assertSeenAndResult(expected, search, search.next_with_ghosts)
1247
1242
        self.assertRaises(StopIteration, next, search)
1248
 
        self.assertEqual({b'head', b'ghost', NULL_REVISION}, search.seen)
 
1243
        self.assertEqual({'head', 'ghost', NULL_REVISION}, search.seen)
1249
1244
        state = search.get_state()
1250
1245
        self.assertEqual(
1251
 
            ({b'ghost', b'head'}, {b'ghost'},
1252
 
                {b'head', NULL_REVISION}),
 
1246
            ({'ghost', 'head'}, {'ghost'},
 
1247
                {'head', NULL_REVISION}),
1253
1248
            state)
1254
1249
 
1255
1250
 
1261
1256
 
1262
1257
    def test_empty_set(self):
1263
1258
        graph = self.make_graph(ancestry_1)
1264
 
        self.assertFindUniqueAncestors(graph, [], b'rev1', [b'rev1'])
1265
 
        self.assertFindUniqueAncestors(graph, [], b'rev2b', [b'rev2b'])
1266
 
        self.assertFindUniqueAncestors(graph, [], b'rev3', [b'rev1', b'rev3'])
 
1259
        self.assertFindUniqueAncestors(graph, [], 'rev1', ['rev1'])
 
1260
        self.assertFindUniqueAncestors(graph, [], 'rev2b', ['rev2b'])
 
1261
        self.assertFindUniqueAncestors(graph, [], 'rev3', ['rev1', 'rev3'])
1267
1262
 
1268
1263
    def test_single_node(self):
1269
1264
        graph = self.make_graph(ancestry_1)
1270
 
        self.assertFindUniqueAncestors(graph, [b'rev2a'], b'rev2a', [b'rev1'])
1271
 
        self.assertFindUniqueAncestors(graph, [b'rev2b'], b'rev2b', [b'rev1'])
1272
 
        self.assertFindUniqueAncestors(graph, [b'rev3'], b'rev3', [b'rev2a'])
 
1265
        self.assertFindUniqueAncestors(graph, ['rev2a'], 'rev2a', ['rev1'])
 
1266
        self.assertFindUniqueAncestors(graph, ['rev2b'], 'rev2b', ['rev1'])
 
1267
        self.assertFindUniqueAncestors(graph, ['rev3'], 'rev3', ['rev2a'])
1273
1268
 
1274
1269
    def test_minimal_ancestry(self):
1275
1270
        graph = self.make_breaking_graph(extended_history_shortcut,
1276
 
                                         [NULL_REVISION, b'a', b'b'])
1277
 
        self.assertFindUniqueAncestors(graph, [b'e'], b'e', [b'd'])
 
1271
                                         [NULL_REVISION, 'a', 'b'])
 
1272
        self.assertFindUniqueAncestors(graph, ['e'], 'e', ['d'])
1278
1273
 
1279
1274
        graph = self.make_breaking_graph(extended_history_shortcut,
1280
 
                                         [b'b'])
1281
 
        self.assertFindUniqueAncestors(graph, [b'f'], b'f', [b'a', b'd'])
 
1275
                                         ['b'])
 
1276
        self.assertFindUniqueAncestors(graph, ['f'], 'f', ['a', 'd'])
1282
1277
 
1283
1278
        graph = self.make_breaking_graph(complex_shortcut,
1284
 
                                         [b'a', b'b'])
1285
 
        self.assertFindUniqueAncestors(graph, [b'h'], b'h', [b'i'])
1286
 
        self.assertFindUniqueAncestors(graph, [b'e', b'g', b'i'], b'i', [b'h'])
1287
 
        self.assertFindUniqueAncestors(graph, [b'h'], b'h', [b'g'])
1288
 
        self.assertFindUniqueAncestors(graph, [b'h'], b'h', [b'j'])
 
1279
                                         ['a', 'b'])
 
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'])
1289
1284
 
1290
1285
    def test_in_ancestry(self):
1291
1286
        graph = self.make_graph(ancestry_1)
1292
 
        self.assertFindUniqueAncestors(graph, [], b'rev1', [b'rev3'])
1293
 
        self.assertFindUniqueAncestors(graph, [], b'rev2b', [b'rev4'])
 
1287
        self.assertFindUniqueAncestors(graph, [], 'rev1', ['rev3'])
 
1288
        self.assertFindUniqueAncestors(graph, [], 'rev2b', ['rev4'])
1294
1289
 
1295
1290
    def test_multiple_revisions(self):
1296
1291
        graph = self.make_graph(ancestry_1)
1297
1292
        self.assertFindUniqueAncestors(graph,
1298
 
                                       [b'rev4'], b'rev4', [b'rev3', b'rev2b'])
 
1293
            ['rev4'], 'rev4', ['rev3', 'rev2b'])
1299
1294
        self.assertFindUniqueAncestors(graph,
1300
 
                                       [b'rev2a', b'rev3', b'rev4'], b'rev4', [b'rev2b'])
 
1295
            ['rev2a', 'rev3', 'rev4'], 'rev4', ['rev2b'])
1301
1296
 
1302
1297
    def test_complex_shortcut(self):
1303
1298
        graph = self.make_graph(complex_shortcut)
1304
1299
        self.assertFindUniqueAncestors(graph,
1305
 
                                       [b'h', b'n'], b'n', [b'm'])
 
1300
            ['h', 'n'], 'n', ['m'])
1306
1301
        self.assertFindUniqueAncestors(graph,
1307
 
                                       [b'e', b'i', b'm'], b'm', [b'n'])
 
1302
            ['e', 'i', 'm'], 'm', ['n'])
1308
1303
 
1309
1304
    def test_complex_shortcut2(self):
1310
1305
        graph = self.make_graph(complex_shortcut2)
1311
1306
        self.assertFindUniqueAncestors(graph,
1312
 
                                       [b'j', b'u'], b'u', [b't'])
 
1307
            ['j', 'u'], 'u', ['t'])
1313
1308
        self.assertFindUniqueAncestors(graph,
1314
 
                                       [b't'], b't', [b'u'])
 
1309
            ['t'], 't', ['u'])
1315
1310
 
1316
1311
    def test_multiple_interesting_unique(self):
1317
1312
        graph = self.make_graph(multiple_interesting_unique)
1318
1313
        self.assertFindUniqueAncestors(graph,
1319
 
                                       [b'j', b'y'], b'y', [b'z'])
 
1314
            ['j', 'y'], 'y', ['z'])
1320
1315
        self.assertFindUniqueAncestors(graph,
1321
 
                                       [b'p', b'z'], b'z', [b'y'])
 
1316
            ['p', 'z'], 'z', ['y'])
1322
1317
 
1323
1318
    def test_racing_shortcuts(self):
1324
1319
        graph = self.make_graph(racing_shortcuts)
1325
1320
        self.assertFindUniqueAncestors(graph,
1326
 
                                       [b'p', b'q', b'z'], b'z', [b'y'])
 
1321
            ['p', 'q', 'z'], 'z', ['y'])
1327
1322
        self.assertFindUniqueAncestors(graph,
1328
 
                                       [b'h', b'i', b'j', b'y'], b'j', [b'z'])
 
1323
            ['h', 'i', 'j', 'y'], 'j', ['z'])
1329
1324
 
1330
1325
 
1331
1326
class TestGraphFindDistanceToNull(TestGraphBase):
1339
1334
    def test_nothing_known(self):
1340
1335
        graph = self.make_graph(ancestry_1)
1341
1336
        self.assertFindDistance(0, graph, NULL_REVISION, [])
1342
 
        self.assertFindDistance(1, graph, b'rev1', [])
1343
 
        self.assertFindDistance(2, graph, b'rev2a', [])
1344
 
        self.assertFindDistance(2, graph, b'rev2b', [])
1345
 
        self.assertFindDistance(3, graph, b'rev3', [])
1346
 
        self.assertFindDistance(4, graph, b'rev4', [])
 
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', [])
1347
1342
 
1348
1343
    def test_rev_is_ghost(self):
1349
1344
        graph = self.make_graph(ancestry_1)
1350
1345
        e = self.assertRaises(errors.GhostRevisionsHaveNoRevno,
1351
 
                              graph.find_distance_to_null, b'rev_missing', [])
1352
 
        self.assertEqual(b'rev_missing', e.revision_id)
1353
 
        self.assertEqual(b'rev_missing', e.ghost_revision_id)
 
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)
1354
1349
 
1355
1350
    def test_ancestor_is_ghost(self):
1356
 
        graph = self.make_graph({b'rev': [b'parent']})
 
1351
        graph = self.make_graph({'rev':['parent']})
1357
1352
        e = self.assertRaises(errors.GhostRevisionsHaveNoRevno,
1358
 
                              graph.find_distance_to_null, b'rev', [])
1359
 
        self.assertEqual(b'rev', e.revision_id)
1360
 
        self.assertEqual(b'parent', e.ghost_revision_id)
 
1353
                              graph.find_distance_to_null, 'rev', [])
 
1354
        self.assertEqual('rev', e.revision_id)
 
1355
        self.assertEqual('parent', e.ghost_revision_id)
1361
1356
 
1362
1357
    def test_known_in_ancestry(self):
1363
1358
        graph = self.make_graph(ancestry_1)
1364
 
        self.assertFindDistance(2, graph, b'rev2a', [(b'rev1', 1)])
1365
 
        self.assertFindDistance(3, graph, b'rev3', [(b'rev2a', 2)])
 
1359
        self.assertFindDistance(2, graph, 'rev2a', [('rev1', 1)])
 
1360
        self.assertFindDistance(3, graph, 'rev3', [('rev2a', 2)])
1366
1361
 
1367
1362
    def test_known_in_ancestry_limits(self):
1368
 
        graph = self.make_breaking_graph(ancestry_1, [b'rev1'])
1369
 
        self.assertFindDistance(4, graph, b'rev4', [(b'rev3', 3)])
 
1363
        graph = self.make_breaking_graph(ancestry_1, ['rev1'])
 
1364
        self.assertFindDistance(4, graph, 'rev4', [('rev3', 3)])
1370
1365
 
1371
1366
    def test_target_is_ancestor(self):
1372
1367
        graph = self.make_graph(ancestry_1)
1373
 
        self.assertFindDistance(2, graph, b'rev2a', [(b'rev3', 3)])
 
1368
        self.assertFindDistance(2, graph, 'rev2a', [('rev3', 3)])
1374
1369
 
1375
1370
    def test_target_is_ancestor_limits(self):
1376
1371
        """We shouldn't search all history if we run into ourselves"""
1377
 
        graph = self.make_breaking_graph(ancestry_1, [b'rev1'])
1378
 
        self.assertFindDistance(3, graph, b'rev3', [(b'rev4', 4)])
 
1372
        graph = self.make_breaking_graph(ancestry_1, ['rev1'])
 
1373
        self.assertFindDistance(3, graph, 'rev3', [('rev4', 4)])
1379
1374
 
1380
1375
    def test_target_parallel_to_known_limits(self):
1381
1376
        # Even though the known revision isn't part of the other ancestry, they
1382
1377
        # eventually converge
1383
 
        graph = self.make_breaking_graph(with_tail, [b'a'])
1384
 
        self.assertFindDistance(6, graph, b'f', [(b'g', 6)])
1385
 
        self.assertFindDistance(7, graph, b'h', [(b'g', 6)])
1386
 
        self.assertFindDistance(8, graph, b'i', [(b'g', 6)])
1387
 
        self.assertFindDistance(6, graph, b'g', [(b'i', 8)])
 
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)])
1388
1383
 
1389
1384
 
1390
1385
class TestFindMergeOrder(TestGraphBase):
1394
1389
 
1395
1390
    def test_parents(self):
1396
1391
        graph = self.make_graph(ancestry_1)
1397
 
        self.assertMergeOrder([b'rev3', b'rev2b'], graph, b'rev4',
1398
 
                              [b'rev3', b'rev2b'])
1399
 
        self.assertMergeOrder([b'rev3', b'rev2b'], graph, b'rev4',
1400
 
                              [b'rev2b', b'rev3'])
 
1392
        self.assertMergeOrder(['rev3', 'rev2b'], graph, 'rev4',
 
1393
                                                        ['rev3', 'rev2b'])
 
1394
        self.assertMergeOrder(['rev3', 'rev2b'], graph, 'rev4',
 
1395
                                                        ['rev2b', 'rev3'])
1401
1396
 
1402
1397
    def test_ancestors(self):
1403
1398
        graph = self.make_graph(ancestry_1)
1404
 
        self.assertMergeOrder([b'rev1', b'rev2b'], graph, b'rev4',
1405
 
                              [b'rev1', b'rev2b'])
1406
 
        self.assertMergeOrder([b'rev1', b'rev2b'], graph, b'rev4',
1407
 
                              [b'rev2b', b'rev1'])
 
1399
        self.assertMergeOrder(['rev1', 'rev2b'], graph, 'rev4',
 
1400
                                                        ['rev1', 'rev2b'])
 
1401
        self.assertMergeOrder(['rev1', 'rev2b'], graph, 'rev4',
 
1402
                                                        ['rev2b', 'rev1'])
1408
1403
 
1409
1404
    def test_shortcut_one_ancestor(self):
1410
1405
        # When we have enough info, we can stop searching
1411
 
        graph = self.make_breaking_graph(
1412
 
            ancestry_1, [b'rev3', b'rev2b', b'rev4'])
 
1406
        graph = self.make_breaking_graph(ancestry_1, ['rev3', 'rev2b', 'rev4'])
1413
1407
        # Single ancestors shortcut right away
1414
 
        self.assertMergeOrder([b'rev3'], graph, b'rev4', [b'rev3'])
 
1408
        self.assertMergeOrder(['rev3'], graph, 'rev4', ['rev3'])
1415
1409
 
1416
1410
    def test_shortcut_after_one_ancestor(self):
1417
 
        graph = self.make_breaking_graph(ancestry_1, [b'rev2a', b'rev2b'])
1418
 
        self.assertMergeOrder([b'rev3', b'rev1'], graph,
1419
 
                              b'rev4', [b'rev1', b'rev3'])
 
1411
        graph = self.make_breaking_graph(ancestry_1, ['rev2a', 'rev2b'])
 
1412
        self.assertMergeOrder(['rev3', 'rev1'], graph, 'rev4', ['rev1', 'rev3'])
1420
1413
 
1421
1414
 
1422
1415
class TestFindDescendants(TestGraphBase):
1423
1416
 
1424
1417
    def test_find_descendants_rev1_rev3(self):
1425
1418
        graph = self.make_graph(ancestry_1)
1426
 
        descendants = graph.find_descendants(b'rev1', b'rev3')
1427
 
        self.assertEqual({b'rev1', b'rev2a', b'rev3'}, descendants)
 
1419
        descendants = graph.find_descendants('rev1', 'rev3')
 
1420
        self.assertEqual({'rev1', 'rev2a', 'rev3'}, descendants)
1428
1421
 
1429
1422
    def test_find_descendants_rev1_rev4(self):
1430
1423
        graph = self.make_graph(ancestry_1)
1431
 
        descendants = graph.find_descendants(b'rev1', b'rev4')
1432
 
        self.assertEqual({b'rev1', b'rev2a', b'rev2b', b'rev3', b'rev4'},
 
1424
        descendants = graph.find_descendants('rev1', 'rev4')
 
1425
        self.assertEqual({'rev1', 'rev2a', 'rev2b', 'rev3', 'rev4'},
1433
1426
                         descendants)
1434
1427
 
1435
1428
    def test_find_descendants_rev2a_rev4(self):
1436
1429
        graph = self.make_graph(ancestry_1)
1437
 
        descendants = graph.find_descendants(b'rev2a', b'rev4')
1438
 
        self.assertEqual({b'rev2a', b'rev3', b'rev4'}, descendants)
1439
 
 
 
1430
        descendants = graph.find_descendants('rev2a', 'rev4')
 
1431
        self.assertEqual({'rev2a', 'rev3', 'rev4'}, descendants)
1440
1432
 
1441
1433
class TestFindLefthandMerger(TestGraphBase):
1442
1434
 
1445
1437
        self.assertEqual(result, graph.find_lefthand_merger(merged, tip))
1446
1438
 
1447
1439
    def test_find_lefthand_merger_rev2b(self):
1448
 
        self.check_merger(b'rev4', ancestry_1, b'rev2b', b'rev4')
 
1440
        self.check_merger('rev4', ancestry_1, 'rev2b', 'rev4')
1449
1441
 
1450
1442
    def test_find_lefthand_merger_rev2a(self):
1451
 
        self.check_merger(b'rev2a', ancestry_1, b'rev2a', b'rev4')
 
1443
        self.check_merger('rev2a', ancestry_1, 'rev2a', 'rev4')
1452
1444
 
1453
1445
    def test_find_lefthand_merger_rev4(self):
1454
 
        self.check_merger(None, ancestry_1, b'rev4', b'rev2a')
 
1446
        self.check_merger(None, ancestry_1, 'rev4', 'rev2a')
1455
1447
 
1456
1448
    def test_find_lefthand_merger_f(self):
1457
 
        self.check_merger(b'i', complex_shortcut, b'f', b'm')
 
1449
        self.check_merger('i', complex_shortcut, 'f', 'm')
1458
1450
 
1459
1451
    def test_find_lefthand_merger_g(self):
1460
 
        self.check_merger(b'i', complex_shortcut, b'g', b'm')
 
1452
        self.check_merger('i', complex_shortcut, 'g', 'm')
1461
1453
 
1462
1454
    def test_find_lefthand_merger_h(self):
1463
 
        self.check_merger(b'n', complex_shortcut, b'h', b'n')
 
1455
        self.check_merger('n', complex_shortcut, 'h', 'n')
1464
1456
 
1465
1457
 
1466
1458
class TestGetChildMap(TestGraphBase):
1467
1459
 
1468
1460
    def test_get_child_map(self):
1469
1461
        graph = self.make_graph(ancestry_1)
1470
 
        child_map = graph.get_child_map([b'rev4', b'rev3', b'rev2a', b'rev2b'])
1471
 
        self.assertEqual({b'rev1': [b'rev2a', b'rev2b'],
1472
 
                          b'rev2a': [b'rev3'],
1473
 
                          b'rev2b': [b'rev4'],
1474
 
                          b'rev3': [b'rev4']},
1475
 
                         child_map)
 
1462
        child_map = graph.get_child_map(['rev4', 'rev3', 'rev2a', 'rev2b'])
 
1463
        self.assertEqual({'rev1': ['rev2a', 'rev2b'],
 
1464
                          'rev2a': ['rev3'],
 
1465
                          'rev2b': ['rev4'],
 
1466
                          'rev3': ['rev4']},
 
1467
                          child_map)
1476
1468
 
1477
1469
 
1478
1470
class TestCachingParentsProvider(tests.TestCase):
1485
1477
 
1486
1478
    def setUp(self):
1487
1479
        super(TestCachingParentsProvider, self).setUp()
1488
 
        dict_pp = _mod_graph.DictParentsProvider({b'a': (b'b',)})
 
1480
        dict_pp = _mod_graph.DictParentsProvider({'a': ('b',)})
1489
1481
        self.inst_pp = InstrumentedParentsProvider(dict_pp)
1490
1482
        self.caching_pp = _mod_graph.CachingParentsProvider(self.inst_pp)
1491
1483
 
1492
1484
    def test_get_parent_map(self):
1493
1485
        """Requesting the same revision should be returned from cache"""
1494
1486
        self.assertEqual({}, self.caching_pp._cache)
1495
 
        self.assertEqual(
1496
 
            {b'a': (b'b',)}, self.caching_pp.get_parent_map([b'a']))
1497
 
        self.assertEqual([b'a'], self.inst_pp.calls)
1498
 
        self.assertEqual(
1499
 
            {b'a': (b'b',)}, self.caching_pp.get_parent_map([b'a']))
 
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']))
1500
1490
        # No new call, as it should have been returned from the cache
1501
 
        self.assertEqual([b'a'], self.inst_pp.calls)
1502
 
        self.assertEqual({b'a': (b'b',)}, self.caching_pp._cache)
 
1491
        self.assertEqual(['a'], self.inst_pp.calls)
 
1492
        self.assertEqual({'a':('b',)}, self.caching_pp._cache)
1503
1493
 
1504
1494
    def test_get_parent_map_not_present(self):
1505
1495
        """The cache should also track when a revision doesn't exist"""
1506
 
        self.assertEqual({}, self.caching_pp.get_parent_map([b'b']))
1507
 
        self.assertEqual([b'b'], self.inst_pp.calls)
1508
 
        self.assertEqual({}, self.caching_pp.get_parent_map([b'b']))
 
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']))
1509
1499
        # No new calls
1510
 
        self.assertEqual([b'b'], self.inst_pp.calls)
 
1500
        self.assertEqual(['b'], self.inst_pp.calls)
1511
1501
 
1512
1502
    def test_get_parent_map_mixed(self):
1513
1503
        """Anything that can be returned from cache, should be"""
1514
 
        self.assertEqual({}, self.caching_pp.get_parent_map([b'b']))
1515
 
        self.assertEqual([b'b'], self.inst_pp.calls)
1516
 
        self.assertEqual({b'a': (b'b',)},
1517
 
                         self.caching_pp.get_parent_map([b'a', b'b']))
1518
 
        self.assertEqual([b'b', b'a'], self.inst_pp.calls)
 
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)
1519
1509
 
1520
1510
    def test_get_parent_map_repeated(self):
1521
1511
        """Asking for the same parent 2x will only forward 1 request."""
1522
 
        self.assertEqual({b'a': (b'b',)},
1523
 
                         self.caching_pp.get_parent_map([b'b', b'a', b'b']))
 
1512
        self.assertEqual({'a':('b',)},
 
1513
                         self.caching_pp.get_parent_map(['b', 'a', 'b']))
1524
1514
        # Use sorted because we don't care about the order, just that each is
1525
1515
        # only present 1 time.
1526
 
        self.assertEqual([b'a', b'b'], sorted(self.inst_pp.calls))
 
1516
        self.assertEqual(['a', 'b'], sorted(self.inst_pp.calls))
1527
1517
 
1528
1518
    def test_note_missing_key(self):
1529
1519
        """After noting that a key is missing it is cached."""
1530
 
        self.caching_pp.note_missing_key(b'b')
1531
 
        self.assertEqual({}, self.caching_pp.get_parent_map([b'b']))
 
1520
        self.caching_pp.note_missing_key('b')
 
1521
        self.assertEqual({}, self.caching_pp.get_parent_map(['b']))
1532
1522
        self.assertEqual([], self.inst_pp.calls)
1533
 
        self.assertEqual({b'b'}, self.caching_pp.missing_keys)
 
1523
        self.assertEqual({'b'}, self.caching_pp.missing_keys)
1534
1524
 
1535
1525
    def test_get_cached_parent_map(self):
1536
 
        self.assertEqual({}, self.caching_pp.get_cached_parent_map([b'a']))
 
1526
        self.assertEqual({}, self.caching_pp.get_cached_parent_map(['a']))
1537
1527
        self.assertEqual([], self.inst_pp.calls)
1538
 
        self.assertEqual(
1539
 
            {b'a': (b'b',)}, self.caching_pp.get_parent_map([b'a']))
1540
 
        self.assertEqual([b'a'], self.inst_pp.calls)
1541
 
        self.assertEqual({b'a': (b'b',)},
1542
 
                         self.caching_pp.get_cached_parent_map([b'a']))
 
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']))
1543
1532
 
1544
1533
 
1545
1534
class TestCachingParentsProviderExtras(tests.TestCaseWithTransport):
1547
1536
 
1548
1537
    def setUp(self):
1549
1538
        super(TestCachingParentsProviderExtras, self).setUp()
1550
 
 
1551
1539
        class ExtraParentsProvider(object):
1552
1540
 
1553
1541
            def get_parent_map(self, keys):
1554
 
                return {b'rev1': [], b'rev2': [b'rev1', ]}
 
1542
                return {'rev1': [], 'rev2': ['rev1',]}
1555
1543
 
1556
1544
        self.inst_pp = InstrumentedParentsProvider(ExtraParentsProvider())
1557
1545
        self.caching_pp = _mod_graph.CachingParentsProvider(
1559
1547
 
1560
1548
    def test_uncached(self):
1561
1549
        self.caching_pp.disable_cache()
1562
 
        self.assertEqual({b'rev1': []},
1563
 
                         self.caching_pp.get_parent_map([b'rev1']))
1564
 
        self.assertEqual([b'rev1'], self.inst_pp.calls)
 
1550
        self.assertEqual({'rev1': []},
 
1551
                         self.caching_pp.get_parent_map(['rev1']))
 
1552
        self.assertEqual(['rev1'], self.inst_pp.calls)
1565
1553
        self.assertIs(None, self.caching_pp._cache)
1566
1554
 
1567
1555
    def test_cache_initially_empty(self):
1568
1556
        self.assertEqual({}, self.caching_pp._cache)
1569
1557
 
1570
1558
    def test_cached(self):
1571
 
        self.assertEqual({b'rev1': []},
1572
 
                         self.caching_pp.get_parent_map([b'rev1']))
1573
 
        self.assertEqual([b'rev1'], self.inst_pp.calls)
1574
 
        self.assertEqual({b'rev1': [], b'rev2': [b'rev1']},
 
1559
        self.assertEqual({'rev1': []},
 
1560
                         self.caching_pp.get_parent_map(['rev1']))
 
1561
        self.assertEqual(['rev1'], self.inst_pp.calls)
 
1562
        self.assertEqual({'rev1': [], 'rev2': ['rev1']},
1575
1563
                         self.caching_pp._cache)
1576
 
        self.assertEqual({b'rev1': []},
1577
 
                         self.caching_pp.get_parent_map([b'rev1']))
1578
 
        self.assertEqual([b'rev1'], self.inst_pp.calls)
 
1564
        self.assertEqual({'rev1': []},
 
1565
                          self.caching_pp.get_parent_map(['rev1']))
 
1566
        self.assertEqual(['rev1'], self.inst_pp.calls)
1579
1567
 
1580
1568
    def test_disable_cache_clears_cache(self):
1581
1569
        # Put something in the cache
1582
 
        self.caching_pp.get_parent_map([b'rev1'])
 
1570
        self.caching_pp.get_parent_map(['rev1'])
1583
1571
        self.assertEqual(2, len(self.caching_pp._cache))
1584
1572
        self.caching_pp.disable_cache()
1585
1573
        self.assertIs(None, self.caching_pp._cache)
1589
1577
        self.assertEqual('Cache enabled when already enabled.', str(e))
1590
1578
 
1591
1579
    def test_cache_misses(self):
1592
 
        self.caching_pp.get_parent_map([b'rev3'])
1593
 
        self.caching_pp.get_parent_map([b'rev3'])
1594
 
        self.assertEqual([b'rev3'], self.inst_pp.calls)
 
1580
        self.caching_pp.get_parent_map(['rev3'])
 
1581
        self.caching_pp.get_parent_map(['rev3'])
 
1582
        self.assertEqual(['rev3'], self.inst_pp.calls)
1595
1583
 
1596
1584
    def test_no_cache_misses(self):
1597
1585
        self.caching_pp.disable_cache()
1598
1586
        self.caching_pp.enable_cache(cache_misses=False)
1599
 
        self.caching_pp.get_parent_map([b'rev3'])
1600
 
        self.caching_pp.get_parent_map([b'rev3'])
1601
 
        self.assertEqual([b'rev3', b'rev3'], self.inst_pp.calls)
 
1587
        self.caching_pp.get_parent_map(['rev3'])
 
1588
        self.caching_pp.get_parent_map(['rev3'])
 
1589
        self.assertEqual(['rev3', 'rev3'], self.inst_pp.calls)
1602
1590
 
1603
1591
    def test_cache_extras(self):
1604
 
        self.assertEqual({}, self.caching_pp.get_parent_map([b'rev3']))
1605
 
        self.assertEqual({b'rev2': [b'rev1']},
1606
 
                         self.caching_pp.get_parent_map([b'rev2']))
1607
 
        self.assertEqual([b'rev3'], self.inst_pp.calls)
 
1592
        self.assertEqual({}, self.caching_pp.get_parent_map(['rev3']))
 
1593
        self.assertEqual({'rev2': ['rev1']},
 
1594
                         self.caching_pp.get_parent_map(['rev2']))
 
1595
        self.assertEqual(['rev3'], self.inst_pp.calls)
1608
1596
 
1609
1597
    def test_extras_using_cached(self):
1610
 
        self.assertEqual({}, self.caching_pp.get_cached_parent_map([b'rev3']))
1611
 
        self.assertEqual({}, self.caching_pp.get_parent_map([b'rev3']))
1612
 
        self.assertEqual({b'rev2': [b'rev1']},
1613
 
                         self.caching_pp.get_cached_parent_map([b'rev2']))
1614
 
        self.assertEqual([b'rev3'], self.inst_pp.calls)
 
1598
        self.assertEqual({}, self.caching_pp.get_cached_parent_map(['rev3']))
 
1599
        self.assertEqual({}, self.caching_pp.get_parent_map(['rev3']))
 
1600
        self.assertEqual({'rev2': ['rev1']},
 
1601
                         self.caching_pp.get_cached_parent_map(['rev2']))
 
1602
        self.assertEqual(['rev3'], self.inst_pp.calls)
 
1603
 
1615
1604
 
1616
1605
 
1617
1606
class TestCollapseLinearRegions(tests.TestCase):
1621
1610
                         _mod_graph.collapse_linear_regions(original))
1622
1611
 
1623
1612
    def test_collapse_nothing(self):
1624
 
        d = {1: [2, 3], 2: [], 3: []}
 
1613
        d = {1:[2, 3], 2:[], 3:[]}
1625
1614
        self.assertCollapsed(d, d)
1626
 
        d = {1: [2], 2: [3, 4], 3: [5], 4: [5], 5: []}
 
1615
        d = {1:[2], 2:[3, 4], 3:[5], 4:[5], 5:[]}
1627
1616
        self.assertCollapsed(d, d)
1628
1617
 
1629
1618
    def test_collapse_chain(self):
1630
1619
        # Any time we have a linear chain, we should be able to collapse
1631
 
        d = {1: [2], 2: [3], 3: [4], 4: [5], 5: []}
1632
 
        self.assertCollapsed({1: [5], 5: []}, d)
1633
 
        d = {5: [4], 4: [3], 3: [2], 2: [1], 1: []}
1634
 
        self.assertCollapsed({5: [1], 1: []}, d)
1635
 
        d = {5: [3], 3: [4], 4: [1], 1: [2], 2: []}
1636
 
        self.assertCollapsed({5: [2], 2: []}, d)
 
1620
        d = {1:[2], 2:[3], 3:[4], 4:[5], 5:[]}
 
1621
        self.assertCollapsed({1:[5], 5:[]}, d)
 
1622
        d = {5:[4], 4:[3], 3:[2], 2:[1], 1:[]}
 
1623
        self.assertCollapsed({5:[1], 1:[]}, d)
 
1624
        d = {5:[3], 3:[4], 4:[1], 1:[2], 2:[]}
 
1625
        self.assertCollapsed({5:[2], 2:[]}, d)
1637
1626
 
1638
1627
    def test_collapse_with_multiple_children(self):
1639
1628
        #    7
1648
1637
        #
1649
1638
        # 4 and 5 cannot be removed because 6 has 2 children
1650
1639
        # 2 and 3 cannot be removed because 1 has 2 parents
1651
 
        d = {1: [2, 3], 2: [4], 4: [6], 3: [5], 5: [6], 6: [7], 7: []}
 
1640
        d = {1:[2, 3], 2:[4], 4:[6], 3:[5], 5:[6], 6:[7], 7:[]}
1652
1641
        self.assertCollapsed(d, d)
1653
1642
 
1654
1643
 
1660
1649
        # B C
1661
1650
        # |/
1662
1651
        # D
1663
 
        d = {(b'D',): [(b'B',), (b'C',)], (b'C',): [(b'A',)],
1664
 
             (b'B',): [(b'A',)], (b'A',): []}
 
1652
        d = {('D',): [('B',), ('C',)], ('C',):[('A',)],
 
1653
             ('B',): [('A',)], ('A',): []}
1665
1654
        g = _mod_graph.Graph(_mod_graph.DictParentsProvider(d))
1666
1655
        graph_thunk = _mod_graph.GraphThunkIdsToKeys(g)
1667
 
        self.assertEqual([b'D'], sorted(graph_thunk.heads([b'D', b'A'])))
1668
 
        self.assertEqual([b'D'], sorted(graph_thunk.heads([b'D', b'B'])))
1669
 
        self.assertEqual([b'D'], sorted(graph_thunk.heads([b'D', b'C'])))
1670
 
        self.assertEqual([b'B', b'C'], sorted(graph_thunk.heads([b'B', b'C'])))
 
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'])))
1671
1660
 
1672
1661
    def test_add_node(self):
1673
 
        d = {(b'C',): [(b'A',)], (b'B',): [(b'A',)], (b'A',): []}
 
1662
        d = {('C',):[('A',)], ('B',): [('A',)], ('A',): []}
1674
1663
        g = _mod_graph.KnownGraph(d)
1675
1664
        graph_thunk = _mod_graph.GraphThunkIdsToKeys(g)
1676
 
        graph_thunk.add_node(b"D", [b"A", b"C"])
1677
 
        self.assertEqual([b'B', b'D'],
1678
 
                         sorted(graph_thunk.heads([b'D', b'B', b'A'])))
 
1665
        graph_thunk.add_node("D", ["A", "C"])
 
1666
        self.assertEqual(['B', 'D'],
 
1667
            sorted(graph_thunk.heads(['D', 'B', 'A'])))
1679
1668
 
1680
1669
    def test_merge_sort(self):
1681
 
        d = {(b'C',): [(b'A',)], (b'B',): [(b'A',)], (b'A',): []}
 
1670
        d = {('C',):[('A',)], ('B',): [('A',)], ('A',): []}
1682
1671
        g = _mod_graph.KnownGraph(d)
1683
1672
        graph_thunk = _mod_graph.GraphThunkIdsToKeys(g)
1684
 
        graph_thunk.add_node(b"D", [b"A", b"C"])
1685
 
        self.assertEqual([(b'C', 0, (2,), False), (b'A', 0, (1,), True)],
1686
 
                         [(n.key, n.merge_depth, n.revno, n.end_of_merge)
1687
 
                          for n in graph_thunk.merge_sort(b'C')])
 
1673
        graph_thunk.add_node("D", ["A", "C"])
 
1674
        self.assertEqual([('C', 0, (2,), False), ('A', 0, (1,), True)],
 
1675
            [(n.key, n.merge_depth, n.revno, n.end_of_merge)
 
1676
                 for n in graph_thunk.merge_sort('C')])
1688
1677
 
1689
1678
 
1690
1679
class TestStackedParentsProvider(tests.TestCase):
1700
1689
        return SharedInstrumentedParentsProvider(pp, self.calls, info)
1701
1690
 
1702
1691
    def test_stacked_parents_provider(self):
1703
 
        parents1 = _mod_graph.DictParentsProvider({b'rev2': [b'rev3']})
1704
 
        parents2 = _mod_graph.DictParentsProvider({b'rev1': [b'rev4']})
 
1692
        parents1 = _mod_graph.DictParentsProvider({'rev2': ['rev3']})
 
1693
        parents2 = _mod_graph.DictParentsProvider({'rev1': ['rev4']})
1705
1694
        stacked = _mod_graph.StackedParentsProvider([parents1, parents2])
1706
 
        self.assertEqual({b'rev1': [b'rev4'], b'rev2': [b'rev3']},
1707
 
                         stacked.get_parent_map([b'rev1', b'rev2']))
1708
 
        self.assertEqual({b'rev2': [b'rev3'], b'rev1': [b'rev4']},
1709
 
                         stacked.get_parent_map([b'rev2', b'rev1']))
1710
 
        self.assertEqual({b'rev2': [b'rev3']},
1711
 
                         stacked.get_parent_map([b'rev2', b'rev2']))
1712
 
        self.assertEqual({b'rev1': [b'rev4']},
1713
 
                         stacked.get_parent_map([b'rev1', b'rev1']))
 
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']))
1714
1703
 
1715
1704
    def test_stacked_parents_provider_overlapping(self):
1716
1705
        # rev2 is availible in both providers.
1717
1706
        # 1
1718
1707
        # |
1719
1708
        # 2
1720
 
        parents1 = _mod_graph.DictParentsProvider({b'rev2': [b'rev1']})
1721
 
        parents2 = _mod_graph.DictParentsProvider({b'rev2': [b'rev1']})
 
1709
        parents1 = _mod_graph.DictParentsProvider({'rev2': ['rev1']})
 
1710
        parents2 = _mod_graph.DictParentsProvider({'rev2': ['rev1']})
1722
1711
        stacked = _mod_graph.StackedParentsProvider([parents1, parents2])
1723
 
        self.assertEqual({b'rev2': [b'rev1']},
1724
 
                         stacked.get_parent_map([b'rev2']))
 
1712
        self.assertEqual({'rev2': ['rev1']},
 
1713
                         stacked.get_parent_map(['rev2']))
1725
1714
 
1726
1715
    def test_handles_no_get_cached_parent_map(self):
1727
1716
        # this shows that we both handle when a provider doesn't implement
1728
1717
        # get_cached_parent_map
1729
 
        pp1 = self.get_shared_provider(b'pp1', {b'rev2': (b'rev1',)},
 
1718
        pp1 = self.get_shared_provider('pp1', {'rev2': ('rev1',)},
1730
1719
                                       has_cached=False)
1731
 
        pp2 = self.get_shared_provider(b'pp2', {b'rev2': (b'rev1',)},
 
1720
        pp2 = self.get_shared_provider('pp2', {'rev2': ('rev1',)},
1732
1721
                                       has_cached=True)
1733
1722
        stacked = _mod_graph.StackedParentsProvider([pp1, pp2])
1734
 
        self.assertEqual({b'rev2': (b'rev1',)},
1735
 
                         stacked.get_parent_map([b'rev2']))
1736
 
        # No call on b'pp1' because it doesn't provide get_cached_parent_map
1737
 
        self.assertEqual([(b'pp2', 'cached', [b'rev2'])], self.calls)
 
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)
1738
1726
 
1739
1727
    def test_query_order(self):
1740
1728
        # We should call get_cached_parent_map on all providers before we call
1741
1729
        # get_parent_map. Further, we should track what entries we have found,
1742
1730
        # and not re-try them.
1743
 
        pp1 = self.get_shared_provider(b'pp1', {b'a': ()}, has_cached=True)
1744
 
        pp2 = self.get_shared_provider(
1745
 
            b'pp2', {b'c': (b'b',)}, has_cached=False)
1746
 
        pp3 = self.get_shared_provider(
1747
 
            b'pp3', {b'b': (b'a',)}, has_cached=True)
 
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)
1748
1734
        stacked = _mod_graph.StackedParentsProvider([pp1, pp2, pp3])
1749
 
        self.assertEqual({b'a': (), b'b': (b'a',), b'c': (b'b',)},
1750
 
                         stacked.get_parent_map([b'a', b'b', b'c', b'd']))
1751
 
        self.assertEqual([(b'pp1', 'cached', [b'a', b'b', b'c', b'd']),
 
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']),
1752
1738
                          # No call to pp2, because it doesn't have cached
1753
 
                          (b'pp3', 'cached', [b'b', b'c', b'd']),
1754
 
                          (b'pp1', [b'c', b'd']),
1755
 
                          (b'pp2', [b'c', b'd']),
1756
 
                          (b'pp3', [b'd']),
1757
 
                          ], self.calls)
 
1739
                          ('pp3', 'cached', ['b', 'c', 'd']),
 
1740
                          ('pp1', ['c', 'd']),
 
1741
                          ('pp2', ['c', 'd']),
 
1742
                          ('pp3', ['d']),
 
1743
                         ], self.calls)