/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: 2020-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

Show diffs side-by-side

added added

removed removed

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